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