1
// Copyright 2013 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
11
"github.com/juju/loggo"
12
"github.com/juju/version"
13
"launchpad.net/gnuflag"
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"
24
var syncTools = sync.SyncTools
26
func newSyncToolsCommand() cmd.Command {
27
return modelcmd.Wrap(&syncToolsCommand{})
30
// syncToolsCommand copies all the tools from the us-east-1 bucket to the local
32
type syncToolsCommand struct {
33
modelcmd.ModelCommandBase
47
var _ cmd.Command = (*syncToolsCommand)(nil)
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.
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
59
# Download the software (version auto-selected) to the model:
60
juju sync-tools --debug
62
# Download a specific version of the software locally:
63
juju sync-tools --debug --version 2.0 --local-dir=/home/ubuntu/sync-tools
65
# Get locally available software to the model:
66
juju sync-tools --debug --source=/home/ubuntu/sync-tools
73
func (c *syncToolsCommand) Info() *cmd.Info {
76
Purpose: "Copy tools from the official tool store into a local model.",
81
func (c *syncToolsCommand) SetFlags(f *gnuflag.FlagSet) {
82
f.BoolVar(&c.allVersions, "all", false, "Copy all versions, not just the latest")
83
f.StringVar(&c.versionStr, "version", "", "Copy a specific major[.minor] version")
84
f.BoolVar(&c.dryRun, "dry-run", false, "Don't copy, just print what would be copied")
85
f.BoolVar(&c.dev, "dev", false, "Consider development versions as well as released ones\n DEPRECATED: use --stream instead")
86
f.BoolVar(&c.public, "public", false, "Tools are for a public cloud, so generate mirrors information")
87
f.StringVar(&c.source, "source", "", "Local source directory")
88
f.StringVar(&c.stream, "stream", "", "Simplestreams stream for which to sync metadata")
89
f.StringVar(&c.localDir, "local-dir", "", "Local destination directory")
90
f.StringVar(&c.destination, "destination", "", "Local destination directory\n DEPRECATED: use --local-dir instead")
93
func (c *syncToolsCommand) Init(args []string) error {
94
if c.destination != "" {
95
// Override localDir with destination as localDir now replaces destination
96
c.localDir = c.destination
97
logger.Infof("Use of the --destination flag is deprecated in 1.18. Please use --local-dir instead.")
99
if c.versionStr != "" {
101
if c.majorVersion, c.minorVersion, err = version.ParseMajorMinor(c.versionStr); err != nil {
106
c.stream = envtools.TestingStream
108
return cmd.CheckEmpty(args)
111
// syncToolsAPI provides an interface with a subset of the
112
// api.Client API. This exists to enable mocking.
113
type syncToolsAPI interface {
114
FindTools(majorVersion, minorVersion int, series, arch string) (params.FindToolsResult, error)
115
UploadTools(r io.ReadSeeker, v version.Binary, series ...string) (coretools.List, error)
119
var getSyncToolsAPI = func(c *syncToolsCommand) (syncToolsAPI, error) {
120
return c.NewAPIClient()
123
func (c *syncToolsCommand) Run(ctx *cmd.Context) (resultErr error) {
124
// Register writer for output on screen.
125
writer := loggo.NewMinimumLevelWriter(
126
cmd.NewCommandLogWriter("juju.environs.sync", ctx.Stdout, ctx.Stderr),
128
loggo.RegisterWriter("synctools", writer)
129
defer loggo.RemoveWriter("synctools")
131
sctx := &sync.SyncContext{
132
AllVersions: c.allVersions,
133
MajorVersion: c.majorVersion,
134
MinorVersion: c.minorVersion,
140
if c.localDir != "" {
141
stor, err := filestorage.NewFileStorageWriter(c.localDir)
145
writeMirrors := envtools.DoNotWriteMirrors
147
writeMirrors = envtools.WriteMirrors
149
sctx.TargetToolsFinder = sync.StorageToolsFinder{Storage: stor}
150
sctx.TargetToolsUploader = sync.StorageToolsUploader{
153
WriteMirrors: writeMirrors,
157
logger.Infof("--public is ignored unless --local-dir is specified")
159
api, err := getSyncToolsAPI(c)
164
adapter := syncToolsAPIAdapter{api}
165
sctx.TargetToolsFinder = adapter
166
sctx.TargetToolsUploader = adapter
168
return block.ProcessBlockedError(syncTools(sctx), block.BlockChange)
171
// syncToolsAPIAdapter implements sync.ToolsFinder and
172
// sync.ToolsUploader, adapting a syncToolsAPI. This
173
// enables the use of sync.SyncTools with the client
175
type syncToolsAPIAdapter struct {
179
func (s syncToolsAPIAdapter) FindTools(majorVersion int, stream string) (coretools.List, error) {
180
result, err := s.syncToolsAPI.FindTools(majorVersion, -1, "", "")
184
if result.Error != nil {
185
if params.IsCodeNotFound(result.Error) {
186
return nil, coretools.ErrNoMatches
188
return nil, result.Error
190
return result.List, nil
193
func (s syncToolsAPIAdapter) UploadTools(toolsDir, stream string, tools *coretools.Tools, data []byte) error {
194
_, err := s.syncToolsAPI.UploadTools(bytes.NewReader(data), tools.Version)