github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/cmd/juju/commands/synctools.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package commands 5 6 import ( 7 "bytes" 8 "io" 9 10 "github.com/juju/cmd" 11 "github.com/juju/loggo" 12 "github.com/juju/version" 13 "launchpad.net/gnuflag" 14 15 "github.com/juju/juju/apiserver/params" 16 "github.com/juju/juju/cmd/juju/block" 17 "github.com/juju/juju/cmd/modelcmd" 18 "github.com/juju/juju/environs/filestorage" 19 "github.com/juju/juju/environs/sync" 20 envtools "github.com/juju/juju/environs/tools" 21 coretools "github.com/juju/juju/tools" 22 ) 23 24 var syncTools = sync.SyncTools 25 26 func newSyncToolsCommand() cmd.Command { 27 return modelcmd.Wrap(&syncToolsCommand{}) 28 } 29 30 // syncToolsCommand copies all the tools from the us-east-1 bucket to the local 31 // bucket. 32 type syncToolsCommand struct { 33 modelcmd.ModelCommandBase 34 allVersions bool 35 versionStr string 36 majorVersion int 37 minorVersion int 38 dryRun bool 39 dev bool 40 public bool 41 source string 42 stream string 43 localDir string 44 destination string 45 } 46 47 var _ cmd.Command = (*syncToolsCommand)(nil) 48 49 func (c *syncToolsCommand) Info() *cmd.Info { 50 return &cmd.Info{ 51 Name: "sync-tools", 52 Purpose: "copy tools from the official tool store into a local model", 53 Doc: ` 54 This copies the Juju tools tarball from the official tools store (located 55 at https://streams.canonical.com/juju) into your model. 56 This is generally done when you want Juju to be able to run without having to 57 access the Internet. Alternatively you can specify a local directory as source. 58 59 Sometimes this is because the model does not have public access, 60 and sometimes you just want to avoid having to access data outside of 61 the local cloud. 62 `, 63 } 64 } 65 66 func (c *syncToolsCommand) SetFlags(f *gnuflag.FlagSet) { 67 f.BoolVar(&c.allVersions, "all", false, "copy all versions, not just the latest") 68 f.StringVar(&c.versionStr, "version", "", "copy a specific major[.minor] version") 69 f.BoolVar(&c.dryRun, "dry-run", false, "don't copy, just print what would be copied") 70 f.BoolVar(&c.dev, "dev", false, "consider development versions as well as released ones\n DEPRECATED: use --stream instead") 71 f.BoolVar(&c.public, "public", false, "tools are for a public cloud, so generate mirrors information") 72 f.StringVar(&c.source, "source", "", "local source directory") 73 f.StringVar(&c.stream, "stream", "", "simplestreams stream for which to sync metadata") 74 f.StringVar(&c.localDir, "local-dir", "", "local destination directory") 75 f.StringVar(&c.destination, "destination", "", "local destination directory") 76 } 77 78 func (c *syncToolsCommand) Init(args []string) error { 79 if c.destination != "" { 80 // Override localDir with destination as localDir now replaces destination 81 c.localDir = c.destination 82 logger.Warningf("Use of the --destination flag is deprecated in 1.18. Please use --local-dir instead.") 83 } 84 if c.versionStr != "" { 85 var err error 86 if c.majorVersion, c.minorVersion, err = version.ParseMajorMinor(c.versionStr); err != nil { 87 return err 88 } 89 } 90 if c.dev { 91 c.stream = envtools.TestingStream 92 } 93 return cmd.CheckEmpty(args) 94 } 95 96 // syncToolsAPI provides an interface with a subset of the 97 // api.Client API. This exists to enable mocking. 98 type syncToolsAPI interface { 99 FindTools(majorVersion, minorVersion int, series, arch string) (params.FindToolsResult, error) 100 UploadTools(r io.ReadSeeker, v version.Binary, series ...string) (coretools.List, error) 101 Close() error 102 } 103 104 var getSyncToolsAPI = func(c *syncToolsCommand) (syncToolsAPI, error) { 105 return c.NewAPIClient() 106 } 107 108 func (c *syncToolsCommand) Run(ctx *cmd.Context) (resultErr error) { 109 // Register writer for output on screen. 110 loggo.RegisterWriter("synctools", cmd.NewCommandLogWriter("juju.environs.sync", ctx.Stdout, ctx.Stderr), loggo.INFO) 111 defer loggo.RemoveWriter("synctools") 112 113 sctx := &sync.SyncContext{ 114 AllVersions: c.allVersions, 115 MajorVersion: c.majorVersion, 116 MinorVersion: c.minorVersion, 117 DryRun: c.dryRun, 118 Stream: c.stream, 119 Source: c.source, 120 } 121 122 if c.localDir != "" { 123 stor, err := filestorage.NewFileStorageWriter(c.localDir) 124 if err != nil { 125 return err 126 } 127 writeMirrors := envtools.DoNotWriteMirrors 128 if c.public { 129 writeMirrors = envtools.WriteMirrors 130 } 131 sctx.TargetToolsFinder = sync.StorageToolsFinder{Storage: stor} 132 sctx.TargetToolsUploader = sync.StorageToolsUploader{ 133 Storage: stor, 134 WriteMetadata: true, 135 WriteMirrors: writeMirrors, 136 } 137 } else { 138 if c.public { 139 logger.Warningf("--public is ignored unless --local-dir is specified") 140 } 141 api, err := getSyncToolsAPI(c) 142 if err != nil { 143 return err 144 } 145 defer api.Close() 146 adapter := syncToolsAPIAdapter{api} 147 sctx.TargetToolsFinder = adapter 148 sctx.TargetToolsUploader = adapter 149 } 150 return block.ProcessBlockedError(syncTools(sctx), block.BlockChange) 151 } 152 153 // syncToolsAPIAdapter implements sync.ToolsFinder and 154 // sync.ToolsUploader, adapting a syncToolsAPI. This 155 // enables the use of sync.SyncTools with the client 156 // API. 157 type syncToolsAPIAdapter struct { 158 syncToolsAPI 159 } 160 161 func (s syncToolsAPIAdapter) FindTools(majorVersion int, stream string) (coretools.List, error) { 162 result, err := s.syncToolsAPI.FindTools(majorVersion, -1, "", "") 163 if err != nil { 164 return nil, err 165 } 166 if result.Error != nil { 167 if params.IsCodeNotFound(result.Error) { 168 return nil, coretools.ErrNoMatches 169 } 170 return nil, result.Error 171 } 172 return result.List, nil 173 } 174 175 func (s syncToolsAPIAdapter) UploadTools(toolsDir, stream string, tools *coretools.Tools, data []byte) error { 176 _, err := s.syncToolsAPI.UploadTools(bytes.NewReader(data), tools.Version) 177 return err 178 }