github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/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/gnuflag" 12 "github.com/juju/loggo" 13 "github.com/juju/version" 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 const synctoolsDoc = ` 50 This copies the Juju agent software from the official tools store (located 51 at https://streams.canonical.com/juju) into a model. It is generally done 52 when the model is without Internet access. 53 54 Instead of the above site, a local directory can be specified as source. 55 The online store will, of course, need to be contacted at some point to get 56 the software. 57 58 Examples: 59 # Download the software (version auto-selected) to the model: 60 juju sync-tools --debug 61 62 # Download a specific version of the software locally: 63 juju sync-tools --debug --version 2.0 --local-dir=/home/ubuntu/sync-tools 64 65 # Get locally available software to the model: 66 juju sync-tools --debug --source=/home/ubuntu/sync-tools 67 68 See also: 69 upgrade-juju 70 71 ` 72 73 func (c *syncToolsCommand) Info() *cmd.Info { 74 return &cmd.Info{ 75 Name: "sync-tools", 76 Purpose: "Copy tools from the official tool store into a local model.", 77 Doc: synctoolsDoc, 78 } 79 } 80 81 func (c *syncToolsCommand) SetFlags(f *gnuflag.FlagSet) { 82 c.ModelCommandBase.SetFlags(f) 83 f.BoolVar(&c.allVersions, "all", false, "Copy all versions, not just the latest") 84 f.StringVar(&c.versionStr, "version", "", "Copy a specific major[.minor] version") 85 f.BoolVar(&c.dryRun, "dry-run", false, "Don't copy, just print what would be copied") 86 f.BoolVar(&c.dev, "dev", false, "Consider development versions as well as released ones\n DEPRECATED: use --stream instead") 87 f.BoolVar(&c.public, "public", false, "Tools are for a public cloud, so generate mirrors information") 88 f.StringVar(&c.source, "source", "", "Local source directory") 89 f.StringVar(&c.stream, "stream", "", "Simplestreams stream for which to sync metadata") 90 f.StringVar(&c.localDir, "local-dir", "", "Local destination directory") 91 f.StringVar(&c.destination, "destination", "", "Local destination directory\n DEPRECATED: use --local-dir instead") 92 } 93 94 func (c *syncToolsCommand) Init(args []string) error { 95 if c.destination != "" { 96 // Override localDir with destination as localDir now replaces destination 97 c.localDir = c.destination 98 logger.Infof("Use of the --destination flag is deprecated in 1.18. Please use --local-dir instead.") 99 } 100 if c.versionStr != "" { 101 var err error 102 if c.majorVersion, c.minorVersion, err = version.ParseMajorMinor(c.versionStr); err != nil { 103 return err 104 } 105 } 106 if c.dev { 107 c.stream = envtools.TestingStream 108 } 109 return cmd.CheckEmpty(args) 110 } 111 112 // syncToolsAPI provides an interface with a subset of the 113 // api.Client API. This exists to enable mocking. 114 type syncToolsAPI interface { 115 FindTools(majorVersion, minorVersion int, series, arch string) (params.FindToolsResult, error) 116 UploadTools(r io.ReadSeeker, v version.Binary, series ...string) (coretools.List, error) 117 Close() error 118 } 119 120 var getSyncToolsAPI = func(c *syncToolsCommand) (syncToolsAPI, error) { 121 return c.NewAPIClient() 122 } 123 124 func (c *syncToolsCommand) Run(ctx *cmd.Context) (resultErr error) { 125 // Register writer for output on screen. 126 writer := loggo.NewMinimumLevelWriter( 127 cmd.NewCommandLogWriter("juju.environs.sync", ctx.Stdout, ctx.Stderr), 128 loggo.INFO) 129 loggo.RegisterWriter("synctools", writer) 130 defer loggo.RemoveWriter("synctools") 131 132 sctx := &sync.SyncContext{ 133 AllVersions: c.allVersions, 134 MajorVersion: c.majorVersion, 135 MinorVersion: c.minorVersion, 136 DryRun: c.dryRun, 137 Stream: c.stream, 138 Source: c.source, 139 } 140 141 if c.localDir != "" { 142 stor, err := filestorage.NewFileStorageWriter(c.localDir) 143 if err != nil { 144 return err 145 } 146 writeMirrors := envtools.DoNotWriteMirrors 147 if c.public { 148 writeMirrors = envtools.WriteMirrors 149 } 150 sctx.TargetToolsFinder = sync.StorageToolsFinder{Storage: stor} 151 sctx.TargetToolsUploader = sync.StorageToolsUploader{ 152 Storage: stor, 153 WriteMetadata: true, 154 WriteMirrors: writeMirrors, 155 } 156 } else { 157 if c.public { 158 logger.Infof("--public is ignored unless --local-dir is specified") 159 } 160 api, err := getSyncToolsAPI(c) 161 if err != nil { 162 return err 163 } 164 defer api.Close() 165 adapter := syncToolsAPIAdapter{api} 166 sctx.TargetToolsFinder = adapter 167 sctx.TargetToolsUploader = adapter 168 } 169 return block.ProcessBlockedError(syncTools(sctx), block.BlockChange) 170 } 171 172 // syncToolsAPIAdapter implements sync.ToolsFinder and 173 // sync.ToolsUploader, adapting a syncToolsAPI. This 174 // enables the use of sync.SyncTools with the client 175 // API. 176 type syncToolsAPIAdapter struct { 177 syncToolsAPI 178 } 179 180 func (s syncToolsAPIAdapter) FindTools(majorVersion int, stream string) (coretools.List, error) { 181 result, err := s.syncToolsAPI.FindTools(majorVersion, -1, "", "") 182 if err != nil { 183 return nil, err 184 } 185 if result.Error != nil { 186 if params.IsCodeNotFound(result.Error) { 187 return nil, coretools.ErrNoMatches 188 } 189 return nil, result.Error 190 } 191 return result.List, nil 192 } 193 194 func (s syncToolsAPIAdapter) UploadTools(toolsDir, stream string, tools *coretools.Tools, data []byte) error { 195 _, err := s.syncToolsAPI.UploadTools(bytes.NewReader(data), tools.Version) 196 return err 197 }