22
22
"github.com/juju/utils/arch"
23
23
"github.com/juju/utils/series"
24
24
"github.com/juju/utils/ssh"
25
"github.com/juju/version"
25
26
goyaml "gopkg.in/yaml.v2"
26
27
"launchpad.net/gnuflag"
28
29
"github.com/juju/juju/agent"
30
"github.com/juju/juju/agent/agentbootstrap"
29
31
agenttools "github.com/juju/juju/agent/tools"
30
32
agentcmd "github.com/juju/juju/cmd/jujud/agent"
31
33
cmdutil "github.com/juju/juju/cmd/jujud/util"
39
41
"github.com/juju/juju/mongo"
40
42
"github.com/juju/juju/network"
41
43
"github.com/juju/juju/state"
44
"github.com/juju/juju/state/binarystorage"
42
45
"github.com/juju/juju/state/cloudimagemetadata"
43
46
"github.com/juju/juju/state/multiwatcher"
44
47
"github.com/juju/juju/state/storage"
45
"github.com/juju/juju/state/toolstorage"
46
48
"github.com/juju/juju/storage/poolmanager"
47
49
"github.com/juju/juju/tools"
48
"github.com/juju/juju/version"
50
jujuversion "github.com/juju/juju/version"
49
51
"github.com/juju/juju/worker/peergrouper"
53
55
initiateMongoServer = peergrouper.InitiateMongoServer
54
agentInitializeState = agent.InitializeState
56
agentInitializeState = agentbootstrap.InitializeState
55
57
sshGenerateKey = ssh.GenerateKey
56
58
newStateStorage = storage.NewStorage
57
59
minSocketTimeout = 1 * time.Minute
62
64
type BootstrapCommand struct {
65
EnvConfig map[string]interface{}
66
BootstrapConstraints constraints.Value
67
EnvironConstraints constraints.Value
68
Hardware instance.HardwareCharacteristics
71
ImageMetadataDir string
67
ControllerModelConfig map[string]interface{}
68
HostedModelConfig map[string]interface{}
69
BootstrapConstraints constraints.Value
70
ModelConstraints constraints.Value
71
Hardware instance.HardwareCharacteristics
74
ImageMetadataDir string
74
77
// NewBootstrapCommand returns a new BootstrapCommand that has been initialized.
89
92
// SetFlags adds the flags for this command to the passed gnuflag.FlagSet.
90
93
func (c *BootstrapCommand) SetFlags(f *gnuflag.FlagSet) {
91
94
c.AgentConf.AddFlags(f)
92
yamlBase64Var(f, &c.EnvConfig, "model-config", "", "initial model configuration (yaml, base64 encoded)")
95
yamlBase64Var(f, &c.ControllerModelConfig, "model-config", "", "controller model configuration (yaml, base64 encoded)")
96
yamlBase64Var(f, &c.HostedModelConfig, "hosted-model-config", "", "initial hosted model configuration (yaml, base64 encoded)")
93
97
f.Var(constraints.ConstraintsValue{Target: &c.BootstrapConstraints}, "bootstrap-constraints", "bootstrap machine constraints (space-separated strings)")
94
f.Var(constraints.ConstraintsValue{Target: &c.EnvironConstraints}, "constraints", "initial constraints (space-separated strings)")
98
f.Var(constraints.ConstraintsValue{Target: &c.ModelConstraints}, "constraints", "initial constraints (space-separated strings)")
95
99
f.Var(&c.Hardware, "hardware", "hardware characteristics (space-separated strings)")
96
100
f.StringVar(&c.InstanceId, "instance-id", "", "unique instance-id for bootstrap machine")
97
101
f.StringVar(&c.AdminUsername, "admin-user", "admin", "set the name for the juju admin user")
101
105
// Init initializes the command for running.
102
106
func (c *BootstrapCommand) Init(args []string) error {
103
if len(c.EnvConfig) == 0 {
107
if len(c.ControllerModelConfig) == 0 {
104
108
return cmdutil.RequiredError("model-config")
110
if len(c.HostedModelConfig) == 0 {
111
return cmdutil.RequiredError("hosted-model-config")
106
113
if c.InstanceId == "" {
107
114
return cmdutil.RequiredError("instance-id")
115
122
// Run initializes state for an environment.
116
123
func (c *BootstrapCommand) Run(_ *cmd.Context) error {
117
envCfg, err := config.New(config.NoDefaults, c.EnvConfig)
124
envCfg, err := config.New(config.NoDefaults, c.ControllerModelConfig)
147
154
// Check to see if a newer agent version has been requested
148
155
// by the bootstrap client.
149
156
desiredVersion, ok := envCfg.AgentVersion()
150
if ok && desiredVersion != version.Current {
157
if ok && desiredVersion != jujuversion.Current {
151
158
// If we have been asked for a newer version, ensure the newer
152
159
// tools can actually be found, or else bootstrap won't complete.
153
160
stream := envtools.PreferredStream(&desiredVersion, envCfg.Development(), envCfg.AgentStream())
164
171
if errors.IsNotFound(toolsErr) {
165
172
// Newer tools not available, so revert to using the tools
166
173
// matching the current agent version.
167
logger.Warningf("newer tools for %q not available, sticking with version %q", desiredVersion, version.Current)
168
newConfigAttrs["agent-version"] = version.Current.String()
174
logger.Warningf("newer tools for %q not available, sticking with version %q", desiredVersion, jujuversion.Current)
175
newConfigAttrs["agent-version"] = jujuversion.Current.String()
169
176
} else if toolsErr != nil {
170
177
logger.Errorf("cannot find newer tools: %v", toolsErr)
271
278
st, m, stateErr = agentInitializeState(
275
agent.BootstrapMachineConfig{
281
envCfg, c.HostedModelConfig,
282
agentbootstrap.BootstrapMachineConfig{
276
283
Addresses: addrs,
277
284
BootstrapConstraints: c.BootstrapConstraints,
278
ModelConstraints: c.EnvironConstraints,
285
ModelConstraints: c.ModelConstraints,
280
287
InstanceId: instanceId,
281
288
Characteristics: c.Hardware,
306
// Populate the GUI archive catalogue.
307
if err := c.populateGUIArchive(st, env); err != nil {
299
311
// Add custom image metadata to environment storage.
300
312
if c.ImageMetadataDir != "" {
301
313
if err := c.saveCustomImageMetadata(st, env); err != nil {
376
388
func (c *BootstrapCommand) populateTools(st *state.State, env environs.Environ) error {
377
389
agentConfig := c.CurrentConfig()
378
390
dataDir := agentConfig.DataDir()
379
392
current := version.Binary{
380
Number: version.Current,
393
Number: jujuversion.Current,
381
394
Arch: arch.HostArch(),
382
395
Series: series.HostSeries(),
394
407
return errors.Trace(err)
397
storage, err := st.ToolsStorage()
410
toolstorage, err := st.ToolsStorage()
399
412
return errors.Trace(err)
401
defer storage.Close()
414
defer toolstorage.Close()
403
416
var toolsVersions []version.Binary
404
417
if strings.HasPrefix(tools.URL, "file://") {
421
434
for _, toolsVersion := range toolsVersions {
422
metadata := toolstorage.Metadata{
423
Version: toolsVersion,
435
metadata := binarystorage.Metadata{
436
Version: toolsVersion.String(),
424
437
Size: tools.Size,
425
438
SHA256: tools.SHA256,
427
440
logger.Debugf("Adding tools: %v", toolsVersion)
428
if err := storage.AddTools(bytes.NewReader(data), metadata); err != nil {
441
if err := toolstorage.Add(bytes.NewReader(data), metadata); err != nil {
429
442
return errors.Trace(err)
448
// populateGUIArchive stores uploaded Juju GUI archive in provider storage
449
// and updates the GUI metadata.
450
func (c *BootstrapCommand) populateGUIArchive(st *state.State, env environs.Environ) error {
451
agentConfig := c.CurrentConfig()
452
dataDir := agentConfig.DataDir()
453
guistorage, err := st.GUIStorage()
455
return errors.Trace(err)
457
defer guistorage.Close()
458
gui, err := agenttools.ReadGUIArchive(dataDir)
460
// TODO frankban: ignore the error for now, as the GUI could not be
461
// there at all. This needs to be changed before merging into master,
462
// return errors.Annotate(err, "cannot fetch GUI info")
465
f, err := os.Open(filepath.Join(agenttools.SharedGUIDir(dataDir), "gui.tar.bz2"))
467
return errors.Annotate(err, "cannot read GUI archive")
470
if err := guistorage.Add(f, binarystorage.Metadata{
471
Version: gui.Version.String(),
475
return errors.Annotate(err, "cannot store GUI archive")
435
480
// storeCustomImageMetadata reads the custom image metadata from disk,
436
481
// and stores the files in environment storage with the same relative