-
Notifications
You must be signed in to change notification settings - Fork 491
/
synctools.go
172 lines (150 loc) · 5.16 KB
/
synctools.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.
package commands
import (
"bytes"
"io"
"os"
"github.com/juju/cmd/v3"
"github.com/juju/errors"
"github.com/juju/gnuflag"
"github.com/juju/loggo"
"github.com/juju/version/v2"
jujucmd "github.com/juju/juju/cmd"
"github.com/juju/juju/cmd/constants"
"github.com/juju/juju/cmd/juju/block"
"github.com/juju/juju/cmd/modelcmd"
"github.com/juju/juju/environs/filestorage"
"github.com/juju/juju/environs/sync"
envtools "github.com/juju/juju/environs/tools"
coretools "github.com/juju/juju/tools"
)
var syncTools = sync.SyncTools
func newSyncAgentBinaryCommand() cmd.Command {
return modelcmd.Wrap(&syncAgentBinaryCommand{})
}
// syncAgentBinaryCommand copies the tool from either official agent binaries store or
// a local directory to the controller.
type syncAgentBinaryCommand struct {
modelcmd.ModelCommandBase
modelcmd.IAASOnlyCommand
versionStr string
targetVersion version.Number
dryRun bool
public bool
source string
stream string
localDir string
syncToolAPI SyncToolAPI
}
var _ cmd.Command = (*syncAgentBinaryCommand)(nil)
const synctoolsDoc = `
This copies the Juju agent software from the official agent binaries store
(located at https://streams.canonical.com/juju) into the controller.
It is generally done when the controller is without Internet access.
Instead of the above site, a local directory can be specified as source.
The online store will, of course, need to be contacted at some point to get
the software.
`
const synctoolsExamples = `
juju sync-agent-binary --debug --agent-version 2.0
juju sync-agent-binary --debug --agent-version 2.0 --local-dir=/home/ubuntu/sync-agent-binary
`
func (c *syncAgentBinaryCommand) Info() *cmd.Info {
return jujucmd.Info(&cmd.Info{
Name: "sync-agent-binary",
Purpose: "Copy agent binaries from the official agent store into a local controller.",
Doc: synctoolsDoc,
Examples: synctoolsExamples,
SeeAlso: []string{
"upgrade-controller",
},
})
}
func (c *syncAgentBinaryCommand) SetFlags(f *gnuflag.FlagSet) {
c.ModelCommandBase.SetFlags(f)
f.StringVar(&c.versionStr, "agent-version", "", "Copy a specific major[.minor] version")
f.BoolVar(&c.dryRun, "dry-run", false, "Don't copy, just print what would be copied")
f.BoolVar(&c.public, "public", false, "Tools are for a public cloud, so generate mirrors information")
f.StringVar(&c.source, "source", "", "Local source directory")
f.StringVar(&c.stream, "stream", "", "Simplestreams stream for which to sync metadata")
f.StringVar(&c.localDir, "local-dir", "", "Local destination directory")
}
func (c *syncAgentBinaryCommand) Init(args []string) error {
if c.versionStr == "" {
return errors.NewNotValid(nil, "--agent-version is required")
}
var err error
if c.targetVersion, err = version.Parse(c.versionStr); err != nil {
return err
}
return cmd.CheckEmpty(args)
}
// SyncToolAPI provides an interface with a subset of the
// modelupgrader.Client API. This exists to enable mocking.
type SyncToolAPI interface {
UploadTools(r io.ReadSeeker, v version.Binary) (coretools.List, error)
Close() error
}
func (c *syncAgentBinaryCommand) getSyncToolAPI() (SyncToolAPI, error) {
if c.syncToolAPI != nil {
return c.syncToolAPI, nil
}
return c.NewModelUpgraderAPIClient()
}
func (c *syncAgentBinaryCommand) Run(ctx *cmd.Context) (resultErr error) {
// Register writer for output on screen.
writer := loggo.NewMinimumLevelWriter(
cmd.NewCommandLogWriter("juju.environs.sync", ctx.Stdout, ctx.Stderr), loggo.INFO,
)
_ = loggo.RegisterWriter("syncagentbinaries", writer)
defer func() { _, _ = loggo.RemoveWriter("syncagentbinaries") }()
if envMetadataSrc := os.Getenv(constants.EnvJujuMetadataSource); c.source == "" && envMetadataSrc != "" {
c.source = envMetadataSrc
ctx.Infof("Using local simple stream source directory %q", c.source)
}
sctx := &sync.SyncContext{
ChosenVersion: c.targetVersion,
DryRun: c.dryRun,
Stream: c.stream,
Source: c.source,
}
if c.localDir != "" {
stor, err := filestorage.NewFileStorageWriter(c.localDir)
if err != nil {
return err
}
writeMirrors := envtools.DoNotWriteMirrors
if c.public {
writeMirrors = envtools.WriteMirrors
}
sctx.TargetToolsFinder = sync.StorageToolsFinder{Storage: stor}
sctx.TargetToolsUploader = sync.StorageToolsUploader{
Storage: stor,
WriteMetadata: true,
WriteMirrors: writeMirrors,
}
} else {
if c.public {
logger.Infof("--public is ignored unless --local-dir is specified")
}
api, err := c.getSyncToolAPI()
if err != nil {
return err
}
defer api.Close()
adapter := syncToolAPIAdapter{api}
sctx.TargetToolsUploader = adapter
}
return block.ProcessBlockedError(syncTools(sctx), block.BlockChange)
}
// syncToolAPIAdapter implements sync.ToolsFinder and
// sync.ToolsUploader, adapting a syncToolAPI. This
// enables the use of sync.SyncTools.
type syncToolAPIAdapter struct {
SyncToolAPI
}
func (s syncToolAPIAdapter) UploadTools(toolsDir, stream string, tools *coretools.Tools, data []byte) error {
_, err := s.SyncToolAPI.UploadTools(bytes.NewReader(data), tools.Version)
return err
}