github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/cmd/jujud/bootstrap_test.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package main 5 6 import ( 7 "encoding/base64" 8 "encoding/json" 9 "fmt" 10 "io" 11 "io/ioutil" 12 "os" 13 "path/filepath" 14 "runtime" 15 "time" 16 17 "github.com/juju/cmd" 18 "github.com/juju/errors" 19 "github.com/juju/loggo" 20 "github.com/juju/names" 21 gitjujutesting "github.com/juju/testing" 22 jc "github.com/juju/testing/checkers" 23 "github.com/juju/utils" 24 "github.com/juju/utils/arch" 25 "github.com/juju/utils/series" 26 "github.com/juju/utils/set" 27 "github.com/juju/version" 28 gc "gopkg.in/check.v1" 29 "gopkg.in/mgo.v2" 30 goyaml "gopkg.in/yaml.v2" 31 32 "github.com/juju/juju/agent" 33 "github.com/juju/juju/agent/agentbootstrap" 34 agenttools "github.com/juju/juju/agent/tools" 35 "github.com/juju/juju/apiserver/params" 36 agenttesting "github.com/juju/juju/cmd/jujud/agent/testing" 37 cmdutil "github.com/juju/juju/cmd/jujud/util" 38 "github.com/juju/juju/cmd/modelcmd" 39 "github.com/juju/juju/constraints" 40 "github.com/juju/juju/environs" 41 "github.com/juju/juju/environs/config" 42 "github.com/juju/juju/environs/filestorage" 43 "github.com/juju/juju/environs/simplestreams" 44 sstesting "github.com/juju/juju/environs/simplestreams/testing" 45 "github.com/juju/juju/environs/storage" 46 envtesting "github.com/juju/juju/environs/testing" 47 envtools "github.com/juju/juju/environs/tools" 48 "github.com/juju/juju/instance" 49 "github.com/juju/juju/juju" 50 jujutesting "github.com/juju/juju/juju/testing" 51 "github.com/juju/juju/mongo" 52 "github.com/juju/juju/network" 53 "github.com/juju/juju/provider/dummy" 54 "github.com/juju/juju/state" 55 "github.com/juju/juju/state/cloudimagemetadata" 56 "github.com/juju/juju/state/multiwatcher" 57 statestorage "github.com/juju/juju/state/storage" 58 statetesting "github.com/juju/juju/state/testing" 59 "github.com/juju/juju/storage/poolmanager" 60 "github.com/juju/juju/testing" 61 "github.com/juju/juju/tools" 62 jujuversion "github.com/juju/juju/version" 63 ) 64 65 // We don't want to use JujuConnSuite because it gives us 66 // an already-bootstrapped environment. 67 type BootstrapSuite struct { 68 testing.BaseSuite 69 gitjujutesting.MgoSuite 70 envcfg *config.Config 71 b64yamlControllerModelConfig string 72 b64yamlHostedModelConfig string 73 instanceId instance.Id 74 dataDir string 75 logDir string 76 mongoOplogSize string 77 fakeEnsureMongo *agenttesting.FakeEnsureMongo 78 bootstrapName string 79 hostedModelUUID string 80 81 toolsStorage storage.Storage 82 } 83 84 var _ = gc.Suite(&BootstrapSuite{}) 85 86 func (s *BootstrapSuite) SetUpSuite(c *gc.C) { 87 storageDir := c.MkDir() 88 restorer := gitjujutesting.PatchValue(&envtools.DefaultBaseURL, storageDir) 89 stor, err := filestorage.NewFileStorageWriter(storageDir) 90 c.Assert(err, jc.ErrorIsNil) 91 s.toolsStorage = stor 92 93 s.BaseSuite.SetUpSuite(c) 94 s.AddCleanup(func(*gc.C) { 95 restorer() 96 }) 97 s.MgoSuite.SetUpSuite(c) 98 s.PatchValue(&jujuversion.Current, testing.FakeVersionNumber) 99 s.makeTestEnv(c) 100 } 101 102 func (s *BootstrapSuite) TearDownSuite(c *gc.C) { 103 s.MgoSuite.TearDownSuite(c) 104 s.BaseSuite.TearDownSuite(c) 105 dummy.Reset(c) 106 } 107 108 func (s *BootstrapSuite) SetUpTest(c *gc.C) { 109 s.BaseSuite.SetUpTest(c) 110 s.PatchValue(&sshGenerateKey, func(name string) (string, string, error) { 111 return "private-key", "public-key", nil 112 }) 113 114 s.MgoSuite.SetUpTest(c) 115 s.dataDir = c.MkDir() 116 s.logDir = c.MkDir() 117 s.mongoOplogSize = "1234" 118 s.fakeEnsureMongo = agenttesting.InstallFakeEnsureMongo(s) 119 s.PatchValue(&initiateMongoServer, s.fakeEnsureMongo.InitiateMongo) 120 121 // Create fake tools.tar.gz and downloaded-tools.txt. 122 current := version.Binary{ 123 Number: jujuversion.Current, 124 Arch: arch.HostArch(), 125 Series: series.HostSeries(), 126 } 127 toolsDir := filepath.FromSlash(agenttools.SharedToolsDir(s.dataDir, current)) 128 err := os.MkdirAll(toolsDir, 0755) 129 c.Assert(err, jc.ErrorIsNil) 130 err = ioutil.WriteFile(filepath.Join(toolsDir, "tools.tar.gz"), nil, 0644) 131 c.Assert(err, jc.ErrorIsNil) 132 s.writeDownloadedTools(c, &tools.Tools{Version: current}) 133 134 // Create fake gui.tar.bz2 and downloaded-gui.txt. 135 guiDir := filepath.FromSlash(agenttools.SharedGUIDir(s.dataDir)) 136 err = os.MkdirAll(guiDir, 0755) 137 c.Assert(err, jc.ErrorIsNil) 138 err = ioutil.WriteFile(filepath.Join(guiDir, "gui.tar.bz2"), nil, 0644) 139 c.Assert(err, jc.ErrorIsNil) 140 s.writeDownloadedGUI(c, &tools.GUIArchive{ 141 Version: version.MustParse("2.0.42"), 142 }) 143 } 144 145 func (s *BootstrapSuite) TearDownTest(c *gc.C) { 146 s.MgoSuite.TearDownTest(c) 147 s.BaseSuite.TearDownTest(c) 148 } 149 150 func (s *BootstrapSuite) writeDownloadedTools(c *gc.C, tools *tools.Tools) { 151 toolsDir := filepath.FromSlash(agenttools.SharedToolsDir(s.dataDir, tools.Version)) 152 err := os.MkdirAll(toolsDir, 0755) 153 c.Assert(err, jc.ErrorIsNil) 154 data, err := json.Marshal(tools) 155 c.Assert(err, jc.ErrorIsNil) 156 err = ioutil.WriteFile(filepath.Join(toolsDir, "downloaded-tools.txt"), data, 0644) 157 c.Assert(err, jc.ErrorIsNil) 158 } 159 160 func (s *BootstrapSuite) writeDownloadedGUI(c *gc.C, gui *tools.GUIArchive) { 161 guiDir := filepath.FromSlash(agenttools.SharedGUIDir(s.dataDir)) 162 err := os.MkdirAll(guiDir, 0755) 163 c.Assert(err, jc.ErrorIsNil) 164 data, err := json.Marshal(gui) 165 c.Assert(err, jc.ErrorIsNil) 166 err = ioutil.WriteFile(filepath.Join(guiDir, "downloaded-gui.txt"), data, 0644) 167 c.Assert(err, jc.ErrorIsNil) 168 } 169 170 func (s *BootstrapSuite) TestGUIArchiveInfoNotFound(c *gc.C) { 171 dir := filepath.FromSlash(agenttools.SharedGUIDir(s.dataDir)) 172 info := filepath.Join(dir, "downloaded-gui.txt") 173 err := os.Remove(info) 174 c.Assert(err, jc.ErrorIsNil) 175 _, cmd, err := s.initBootstrapCommand( 176 c, nil, "--model-config", s.b64yamlControllerModelConfig, 177 "--hosted-model-config", s.b64yamlHostedModelConfig, 178 "--instance-id", string(s.instanceId)) 179 c.Assert(err, jc.ErrorIsNil) 180 181 var tw loggo.TestWriter 182 err = loggo.RegisterWriter("bootstrap-test", &tw, loggo.DEBUG) 183 c.Assert(err, jc.ErrorIsNil) 184 defer loggo.RemoveWriter("bootstrap-test") 185 186 err = cmd.Run(nil) 187 c.Assert(tw.Log(), jc.LogMatches, jc.SimpleMessages{{ 188 loggo.WARNING, 189 `cannot set up Juju GUI: cannot fetch GUI info: GUI metadata not found`, 190 }}) 191 } 192 193 func (s *BootstrapSuite) TestGUIArchiveInfoError(c *gc.C) { 194 if runtime.GOOS == "windows" { 195 // TODO frankban: skipping for now due to chmod problems with mode 0000 196 // on Windows. We will re-enable this test after further investigation: 197 // "jujud bootstrap" is never run on Windows anyway. 198 c.Skip("needs chmod investigation") 199 } 200 dir := filepath.FromSlash(agenttools.SharedGUIDir(s.dataDir)) 201 info := filepath.Join(dir, "downloaded-gui.txt") 202 err := os.Chmod(info, 0000) 203 c.Assert(err, jc.ErrorIsNil) 204 defer os.Chmod(info, 0600) 205 _, cmd, err := s.initBootstrapCommand( 206 c, nil, "--model-config", s.b64yamlControllerModelConfig, 207 "--hosted-model-config", s.b64yamlHostedModelConfig, 208 "--instance-id", string(s.instanceId)) 209 c.Assert(err, jc.ErrorIsNil) 210 211 var tw loggo.TestWriter 212 err = loggo.RegisterWriter("bootstrap-test", &tw, loggo.DEBUG) 213 c.Assert(err, jc.ErrorIsNil) 214 defer loggo.RemoveWriter("bootstrap-test") 215 216 err = cmd.Run(nil) 217 c.Assert(err, jc.ErrorIsNil) 218 c.Assert(tw.Log(), jc.LogMatches, jc.SimpleMessages{{ 219 loggo.WARNING, 220 `cannot set up Juju GUI: cannot fetch GUI info: cannot read GUI metadata in tools directory: .*`, 221 }}) 222 } 223 224 func (s *BootstrapSuite) TestGUIArchiveError(c *gc.C) { 225 dir := filepath.FromSlash(agenttools.SharedGUIDir(s.dataDir)) 226 archive := filepath.Join(dir, "gui.tar.bz2") 227 err := os.Remove(archive) 228 c.Assert(err, jc.ErrorIsNil) 229 _, cmd, err := s.initBootstrapCommand( 230 c, nil, "--model-config", s.b64yamlControllerModelConfig, 231 "--hosted-model-config", s.b64yamlHostedModelConfig, 232 "--instance-id", string(s.instanceId)) 233 c.Assert(err, jc.ErrorIsNil) 234 235 var tw loggo.TestWriter 236 err = loggo.RegisterWriter("bootstrap-test", &tw, loggo.DEBUG) 237 c.Assert(err, jc.ErrorIsNil) 238 defer loggo.RemoveWriter("bootstrap-test") 239 240 err = cmd.Run(nil) 241 c.Assert(tw.Log(), jc.LogMatches, jc.SimpleMessages{{ 242 loggo.WARNING, 243 `cannot set up Juju GUI: cannot read GUI archive: .*`, 244 }}) 245 } 246 247 func (s *BootstrapSuite) TestGUIArchiveSuccess(c *gc.C) { 248 _, cmd, err := s.initBootstrapCommand( 249 c, nil, "--model-config", s.b64yamlControllerModelConfig, 250 "--hosted-model-config", s.b64yamlHostedModelConfig, 251 "--instance-id", string(s.instanceId)) 252 c.Assert(err, jc.ErrorIsNil) 253 254 var tw loggo.TestWriter 255 err = loggo.RegisterWriter("bootstrap-test", &tw, loggo.DEBUG) 256 c.Assert(err, jc.ErrorIsNil) 257 defer loggo.RemoveWriter("bootstrap-test") 258 259 err = cmd.Run(nil) 260 c.Assert(err, jc.ErrorIsNil) 261 c.Assert(tw.Log(), jc.LogMatches, jc.SimpleMessages{{ 262 loggo.DEBUG, 263 `Juju GUI successfully set up`, 264 }}) 265 266 // Retrieve the state so that it is possible to access the GUI storage. 267 st, err := state.Open(testing.ModelTag, &mongo.MongoInfo{ 268 Info: mongo.Info{ 269 Addrs: []string{gitjujutesting.MgoServer.Addr()}, 270 CACert: testing.CACert, 271 }, 272 Password: testPassword, 273 }, mongo.DefaultDialOpts(), environs.NewStatePolicy()) 274 c.Assert(err, jc.ErrorIsNil) 275 defer st.Close() 276 277 // The GUI archive has been uploaded to the GUI storage. 278 storage, err := st.GUIStorage() 279 c.Assert(err, jc.ErrorIsNil) 280 defer storage.Close() 281 allMeta, err := storage.AllMetadata() 282 c.Assert(err, jc.ErrorIsNil) 283 c.Assert(allMeta, gc.HasLen, 1) 284 c.Assert(allMeta[0].Version, gc.Equals, "2.0.42") 285 286 // The current GUI version has been set. 287 vers, err := st.GUIVersion() 288 c.Assert(err, jc.ErrorIsNil) 289 c.Assert(vers.String(), gc.Equals, "2.0.42") 290 } 291 292 var testPassword = "my-admin-secret" 293 294 func (s *BootstrapSuite) initBootstrapCommand(c *gc.C, jobs []multiwatcher.MachineJob, args ...string) (machineConf agent.ConfigSetterWriter, cmd *BootstrapCommand, err error) { 295 if len(jobs) == 0 { 296 // Add default jobs. 297 jobs = []multiwatcher.MachineJob{ 298 multiwatcher.JobManageModel, 299 multiwatcher.JobHostUnits, 300 multiwatcher.JobManageNetworking, 301 } 302 } 303 // NOTE: the old test used an equivalent of the NewAgentConfig, but it 304 // really should be using NewStateMachineConfig. 305 agentParams := agent.AgentConfigParams{ 306 Paths: agent.Paths{ 307 LogDir: s.logDir, 308 DataDir: s.dataDir, 309 }, 310 Jobs: jobs, 311 Tag: names.NewMachineTag("0"), 312 UpgradedToVersion: jujuversion.Current, 313 Password: testPassword, 314 Nonce: agent.BootstrapNonce, 315 Model: testing.ModelTag, 316 StateAddresses: []string{gitjujutesting.MgoServer.Addr()}, 317 APIAddresses: []string{"0.1.2.3:1234"}, 318 CACert: testing.CACert, 319 Values: map[string]string{ 320 agent.Namespace: "foobar", 321 agent.MongoOplogSize: s.mongoOplogSize, 322 }, 323 } 324 servingInfo := params.StateServingInfo{ 325 Cert: "some cert", 326 PrivateKey: "some key", 327 CAPrivateKey: "another key", 328 APIPort: 3737, 329 StatePort: gitjujutesting.MgoServer.Port(), 330 } 331 332 machineConf, err = agent.NewStateMachineConfig(agentParams, servingInfo) 333 c.Assert(err, jc.ErrorIsNil) 334 err = machineConf.Write() 335 c.Assert(err, jc.ErrorIsNil) 336 337 cmd = NewBootstrapCommand() 338 339 err = testing.InitCommand(cmd, append([]string{"--data-dir", s.dataDir}, args...)) 340 return machineConf, cmd, err 341 } 342 343 func (s *BootstrapSuite) TestInitializeEnvironment(c *gc.C) { 344 hw := instance.MustParseHardware("arch=amd64 mem=8G") 345 machConf, cmd, err := s.initBootstrapCommand( 346 c, nil, "--model-config", s.b64yamlControllerModelConfig, 347 "--hosted-model-config", s.b64yamlHostedModelConfig, 348 "--instance-id", string(s.instanceId), "--hardware", hw.String(), 349 ) 350 c.Assert(err, jc.ErrorIsNil) 351 err = cmd.Run(nil) 352 c.Assert(err, jc.ErrorIsNil) 353 354 c.Assert(s.fakeEnsureMongo.DataDir, gc.Equals, s.dataDir) 355 c.Assert(s.fakeEnsureMongo.InitiateCount, gc.Equals, 1) 356 c.Assert(s.fakeEnsureMongo.EnsureCount, gc.Equals, 1) 357 c.Assert(s.fakeEnsureMongo.DataDir, gc.Equals, s.dataDir) 358 c.Assert(s.fakeEnsureMongo.OplogSize, gc.Equals, 1234) 359 360 expectInfo, exists := machConf.StateServingInfo() 361 c.Assert(exists, jc.IsTrue) 362 c.Assert(expectInfo.SharedSecret, gc.Equals, "") 363 c.Assert(expectInfo.SystemIdentity, gc.Equals, "") 364 365 servingInfo := s.fakeEnsureMongo.Info 366 c.Assert(len(servingInfo.SharedSecret), gc.Not(gc.Equals), 0) 367 c.Assert(len(servingInfo.SystemIdentity), gc.Not(gc.Equals), 0) 368 servingInfo.SharedSecret = "" 369 servingInfo.SystemIdentity = "" 370 expect := cmdutil.ParamsStateServingInfoToStateStateServingInfo(expectInfo) 371 c.Assert(servingInfo, jc.DeepEquals, expect) 372 expectDialAddrs := []string{fmt.Sprintf("127.0.0.1:%d", expectInfo.StatePort)} 373 gotDialAddrs := s.fakeEnsureMongo.InitiateParams.DialInfo.Addrs 374 c.Assert(gotDialAddrs, gc.DeepEquals, expectDialAddrs) 375 376 c.Assert(s.fakeEnsureMongo.InitiateParams.MemberHostPort, gc.Equals, expectDialAddrs[0]) 377 c.Assert(s.fakeEnsureMongo.InitiateParams.User, gc.Equals, "") 378 c.Assert(s.fakeEnsureMongo.InitiateParams.Password, gc.Equals, "") 379 380 st, err := state.Open(testing.ModelTag, &mongo.MongoInfo{ 381 Info: mongo.Info{ 382 Addrs: []string{gitjujutesting.MgoServer.Addr()}, 383 CACert: testing.CACert, 384 }, 385 Password: testPassword, 386 }, mongo.DefaultDialOpts(), environs.NewStatePolicy()) 387 c.Assert(err, jc.ErrorIsNil) 388 defer st.Close() 389 machines, err := st.AllMachines() 390 c.Assert(err, jc.ErrorIsNil) 391 c.Assert(machines, gc.HasLen, 1) 392 393 instid, err := machines[0].InstanceId() 394 c.Assert(err, jc.ErrorIsNil) 395 c.Assert(instid, gc.Equals, instance.Id(string(s.instanceId))) 396 397 stateHw, err := machines[0].HardwareCharacteristics() 398 c.Assert(err, jc.ErrorIsNil) 399 c.Assert(stateHw, gc.NotNil) 400 c.Assert(*stateHw, gc.DeepEquals, hw) 401 402 cons, err := st.ModelConstraints() 403 c.Assert(err, jc.ErrorIsNil) 404 c.Assert(&cons, jc.Satisfies, constraints.IsEmpty) 405 406 cfg, err := st.ModelConfig() 407 c.Assert(err, jc.ErrorIsNil) 408 c.Assert(cfg.AuthorizedKeys(), gc.Equals, s.envcfg.AuthorizedKeys()+"\npublic-key") 409 } 410 411 func (s *BootstrapSuite) TestInitializeEnvironmentInvalidOplogSize(c *gc.C) { 412 s.mongoOplogSize = "NaN" 413 hw := instance.MustParseHardware("arch=amd64 mem=8G") 414 _, cmd, err := s.initBootstrapCommand( 415 c, nil, "--model-config", s.b64yamlControllerModelConfig, 416 "--hosted-model-config", s.b64yamlHostedModelConfig, 417 "--instance-id", string(s.instanceId), "--hardware", hw.String(), 418 ) 419 c.Assert(err, jc.ErrorIsNil) 420 err = cmd.Run(nil) 421 c.Assert(err, gc.ErrorMatches, `invalid oplog size: "NaN"`) 422 } 423 424 func (s *BootstrapSuite) TestInitializeEnvironmentToolsNotFound(c *gc.C) { 425 // bootstrap with 1.99.1 but there will be no tools so version will be reset. 426 envcfg, err := s.envcfg.Apply(map[string]interface{}{ 427 "agent-version": "1.99.1", 428 }) 429 c.Assert(err, jc.ErrorIsNil) 430 b64yamlControllerModelConfig := b64yaml(envcfg.AllAttrs()).encode() 431 432 hw := instance.MustParseHardware("arch=amd64 mem=8G") 433 _, cmd, err := s.initBootstrapCommand( 434 c, nil, "--model-config", b64yamlControllerModelConfig, 435 "--hosted-model-config", s.b64yamlHostedModelConfig, 436 "--instance-id", string(s.instanceId), "--hardware", hw.String(), 437 ) 438 c.Assert(err, jc.ErrorIsNil) 439 err = cmd.Run(nil) 440 c.Assert(err, jc.ErrorIsNil) 441 442 st, err := state.Open(testing.ModelTag, &mongo.MongoInfo{ 443 Info: mongo.Info{ 444 Addrs: []string{gitjujutesting.MgoServer.Addr()}, 445 CACert: testing.CACert, 446 }, 447 Password: testPassword, 448 }, mongo.DefaultDialOpts(), environs.NewStatePolicy()) 449 c.Assert(err, jc.ErrorIsNil) 450 defer st.Close() 451 452 cfg, err := st.ModelConfig() 453 c.Assert(err, jc.ErrorIsNil) 454 vers, ok := cfg.AgentVersion() 455 c.Assert(ok, jc.IsTrue) 456 c.Assert(vers.String(), gc.Equals, "1.99.0") 457 } 458 459 func (s *BootstrapSuite) TestSetConstraints(c *gc.C) { 460 bootstrapCons := constraints.Value{Mem: uint64p(4096), CpuCores: uint64p(4)} 461 modelCons := constraints.Value{Mem: uint64p(2048), CpuCores: uint64p(2)} 462 _, cmd, err := s.initBootstrapCommand(c, nil, 463 "--model-config", s.b64yamlControllerModelConfig, 464 "--hosted-model-config", s.b64yamlHostedModelConfig, 465 "--instance-id", string(s.instanceId), 466 "--bootstrap-constraints", bootstrapCons.String(), 467 "--constraints", modelCons.String(), 468 ) 469 c.Assert(err, jc.ErrorIsNil) 470 err = cmd.Run(nil) 471 c.Assert(err, jc.ErrorIsNil) 472 473 st, err := state.Open(testing.ModelTag, &mongo.MongoInfo{ 474 Info: mongo.Info{ 475 Addrs: []string{gitjujutesting.MgoServer.Addr()}, 476 CACert: testing.CACert, 477 }, 478 Password: testPassword, 479 }, mongo.DefaultDialOpts(), environs.NewStatePolicy()) 480 c.Assert(err, jc.ErrorIsNil) 481 defer st.Close() 482 483 cons, err := st.ModelConstraints() 484 c.Assert(err, jc.ErrorIsNil) 485 c.Assert(cons, gc.DeepEquals, modelCons) 486 487 machines, err := st.AllMachines() 488 c.Assert(err, jc.ErrorIsNil) 489 c.Assert(machines, gc.HasLen, 1) 490 cons, err = machines[0].Constraints() 491 c.Assert(err, jc.ErrorIsNil) 492 c.Assert(cons, gc.DeepEquals, bootstrapCons) 493 } 494 495 func uint64p(v uint64) *uint64 { 496 return &v 497 } 498 499 func (s *BootstrapSuite) TestDefaultMachineJobs(c *gc.C) { 500 expectedJobs := []state.MachineJob{ 501 state.JobManageModel, 502 state.JobHostUnits, 503 state.JobManageNetworking, 504 } 505 _, cmd, err := s.initBootstrapCommand(c, nil, 506 "--model-config", s.b64yamlControllerModelConfig, 507 "--hosted-model-config", s.b64yamlHostedModelConfig, 508 "--instance-id", string(s.instanceId), 509 ) 510 c.Assert(err, jc.ErrorIsNil) 511 err = cmd.Run(nil) 512 c.Assert(err, jc.ErrorIsNil) 513 514 st, err := state.Open(testing.ModelTag, &mongo.MongoInfo{ 515 Info: mongo.Info{ 516 Addrs: []string{gitjujutesting.MgoServer.Addr()}, 517 CACert: testing.CACert, 518 }, 519 Password: testPassword, 520 }, mongo.DefaultDialOpts(), environs.NewStatePolicy()) 521 c.Assert(err, jc.ErrorIsNil) 522 defer st.Close() 523 m, err := st.Machine("0") 524 c.Assert(err, jc.ErrorIsNil) 525 c.Assert(m.Jobs(), gc.DeepEquals, expectedJobs) 526 } 527 528 func (s *BootstrapSuite) TestConfiguredMachineJobs(c *gc.C) { 529 jobs := []multiwatcher.MachineJob{multiwatcher.JobManageModel} 530 _, cmd, err := s.initBootstrapCommand(c, jobs, 531 "--model-config", s.b64yamlControllerModelConfig, 532 "--hosted-model-config", s.b64yamlHostedModelConfig, 533 "--instance-id", string(s.instanceId), 534 ) 535 c.Assert(err, jc.ErrorIsNil) 536 err = cmd.Run(nil) 537 c.Assert(err, jc.ErrorIsNil) 538 539 st, err := state.Open(testing.ModelTag, &mongo.MongoInfo{ 540 Info: mongo.Info{ 541 Addrs: []string{gitjujutesting.MgoServer.Addr()}, 542 CACert: testing.CACert, 543 }, 544 Password: testPassword, 545 }, mongo.DefaultDialOpts(), environs.NewStatePolicy()) 546 c.Assert(err, jc.ErrorIsNil) 547 defer st.Close() 548 m, err := st.Machine("0") 549 c.Assert(err, jc.ErrorIsNil) 550 c.Assert(m.Jobs(), gc.DeepEquals, []state.MachineJob{state.JobManageModel}) 551 } 552 553 func testOpenState(c *gc.C, info *mongo.MongoInfo, expectErrType error) { 554 st, err := state.Open(testing.ModelTag, info, mongo.DefaultDialOpts(), environs.NewStatePolicy()) 555 if st != nil { 556 st.Close() 557 } 558 if expectErrType != nil { 559 c.Assert(err, gc.FitsTypeOf, expectErrType) 560 } else { 561 c.Assert(err, jc.ErrorIsNil) 562 } 563 } 564 565 func (s *BootstrapSuite) TestInitialPassword(c *gc.C) { 566 machineConf, cmd, err := s.initBootstrapCommand(c, nil, 567 "--model-config", s.b64yamlControllerModelConfig, 568 "--hosted-model-config", s.b64yamlHostedModelConfig, 569 "--instance-id", string(s.instanceId), 570 ) 571 c.Assert(err, jc.ErrorIsNil) 572 573 err = cmd.Run(nil) 574 c.Assert(err, jc.ErrorIsNil) 575 576 info := &mongo.MongoInfo{ 577 Info: mongo.Info{ 578 Addrs: []string{gitjujutesting.MgoServer.Addr()}, 579 CACert: testing.CACert, 580 }, 581 } 582 583 // Check we can log in to mongo as admin. 584 // TODO(dfc) does passing nil for the admin user name make your skin crawl ? mine too. 585 info.Tag, info.Password = nil, testPassword 586 st, err := state.Open(testing.ModelTag, info, mongo.DefaultDialOpts(), environs.NewStatePolicy()) 587 c.Assert(err, jc.ErrorIsNil) 588 defer st.Close() 589 590 // We're running Mongo with --noauth; let's explicitly verify 591 // that we can login as that user. Even with --noauth, an 592 // explicit Login will still be verified. 593 adminDB := st.MongoSession().DB("admin") 594 err = adminDB.Login("admin", "invalid-password") 595 c.Assert(err, gc.ErrorMatches, "auth fail(s|ed)") 596 err = adminDB.Login("admin", info.Password) 597 c.Assert(err, jc.ErrorIsNil) 598 599 // Check that the admin user has been given an appropriate 600 // password 601 u, err := st.User(names.NewLocalUserTag("admin")) 602 c.Assert(err, jc.ErrorIsNil) 603 c.Assert(u.PasswordValid(testPassword), jc.IsTrue) 604 605 // Check that the machine configuration has been given a new 606 // password and that we can connect to mongo as that machine 607 // and that the in-mongo password also verifies correctly. 608 machineConf1, err := agent.ReadConfig(agent.ConfigPath(machineConf.DataDir(), names.NewMachineTag("0"))) 609 c.Assert(err, jc.ErrorIsNil) 610 611 stateinfo, ok := machineConf1.MongoInfo() 612 c.Assert(ok, jc.IsTrue) 613 st, err = state.Open(testing.ModelTag, stateinfo, mongo.DefaultDialOpts(), environs.NewStatePolicy()) 614 c.Assert(err, jc.ErrorIsNil) 615 defer st.Close() 616 617 m, err := st.Machine("0") 618 c.Assert(err, jc.ErrorIsNil) 619 c.Assert(m.HasVote(), jc.IsTrue) 620 } 621 622 var bootstrapArgTests = []struct { 623 input []string 624 err string 625 expectedInstanceId string 626 expectedHardware instance.HardwareCharacteristics 627 expectedConfig map[string]interface{} 628 }{ 629 { 630 // no value supplied for model-config 631 err: "--model-config option must be set", 632 }, { 633 // empty model-config 634 input: []string{"--model-config", ""}, 635 err: "--model-config option must be set", 636 }, { 637 // wrong, should be base64 638 input: []string{"--model-config", "name: banana\n"}, 639 err: ".*illegal base64 data at input byte.*", 640 }, { 641 // no value supplied for hosted-model-config 642 input: []string{ 643 "--model-config", base64.StdEncoding.EncodeToString([]byte("name: banana\n")), 644 }, 645 err: "--hosted-model-config option must be set", 646 }, { 647 // no value supplied for instance-id 648 input: []string{ 649 "--model-config", base64.StdEncoding.EncodeToString([]byte("name: banana\n")), 650 "--hosted-model-config", base64.StdEncoding.EncodeToString([]byte("name: banana\n")), 651 }, 652 err: "--instance-id option must be set", 653 }, { 654 // empty instance-id 655 input: []string{ 656 "--model-config", base64.StdEncoding.EncodeToString([]byte("name: banana\n")), 657 "--hosted-model-config", base64.StdEncoding.EncodeToString([]byte("name: banana\n")), 658 "--instance-id", "", 659 }, 660 err: "--instance-id option must be set", 661 }, { 662 input: []string{ 663 "--model-config", base64.StdEncoding.EncodeToString([]byte("name: banana\n")), 664 "--hosted-model-config", base64.StdEncoding.EncodeToString([]byte("name: banana\n")), 665 "--instance-id", "anything", 666 }, 667 expectedInstanceId: "anything", 668 expectedConfig: map[string]interface{}{"name": "banana"}, 669 }, { 670 input: []string{ 671 "--model-config", base64.StdEncoding.EncodeToString([]byte("name: banana\n")), 672 "--hosted-model-config", base64.StdEncoding.EncodeToString([]byte("name: banana\n")), 673 "--instance-id", "anything", 674 "--hardware", "nonsense", 675 }, 676 err: `invalid value "nonsense" for flag --hardware: malformed characteristic "nonsense"`, 677 }, { 678 input: []string{ 679 "--model-config", base64.StdEncoding.EncodeToString([]byte("name: banana\n")), 680 "--hosted-model-config", base64.StdEncoding.EncodeToString([]byte("name: banana\n")), 681 "--instance-id", "anything", 682 "--hardware", "arch=amd64 cpu-cores=4 root-disk=2T", 683 }, 684 expectedInstanceId: "anything", 685 expectedHardware: instance.MustParseHardware("arch=amd64 cpu-cores=4 root-disk=2T"), 686 expectedConfig: map[string]interface{}{"name": "banana"}, 687 }, 688 } 689 690 func (s *BootstrapSuite) TestBootstrapArgs(c *gc.C) { 691 for i, t := range bootstrapArgTests { 692 c.Logf("test %d", i) 693 var args []string 694 args = append(args, t.input...) 695 _, cmd, err := s.initBootstrapCommand(c, nil, args...) 696 if t.err == "" { 697 c.Assert(cmd, gc.NotNil) 698 c.Assert(err, jc.ErrorIsNil) 699 c.Assert(cmd.ControllerModelConfig, gc.DeepEquals, t.expectedConfig) 700 c.Assert(cmd.InstanceId, gc.Equals, t.expectedInstanceId) 701 c.Assert(cmd.Hardware, gc.DeepEquals, t.expectedHardware) 702 } else { 703 c.Assert(err, gc.ErrorMatches, t.err) 704 } 705 } 706 } 707 708 func (s *BootstrapSuite) TestInitializeStateArgs(c *gc.C) { 709 var called int 710 initializeState := func(_ names.UserTag, _ agent.ConfigSetter, envCfg *config.Config, hostedModelConfig map[string]interface{}, machineCfg agentbootstrap.BootstrapMachineConfig, dialOpts mongo.DialOpts, policy state.Policy) (_ *state.State, _ *state.Machine, resultErr error) { 711 called++ 712 c.Assert(dialOpts.Direct, jc.IsTrue) 713 c.Assert(dialOpts.Timeout, gc.Equals, 30*time.Second) 714 c.Assert(dialOpts.SocketTimeout, gc.Equals, 123*time.Second) 715 c.Assert(hostedModelConfig, jc.DeepEquals, map[string]interface{}{ 716 "name": "hosted-model", 717 "uuid": s.hostedModelUUID, 718 }) 719 return nil, nil, errors.New("failed to initialize state") 720 } 721 s.PatchValue(&agentInitializeState, initializeState) 722 _, cmd, err := s.initBootstrapCommand(c, nil, 723 "--model-config", s.b64yamlControllerModelConfig, 724 "--hosted-model-config", s.b64yamlHostedModelConfig, 725 "--instance-id", string(s.instanceId), 726 ) 727 c.Assert(err, jc.ErrorIsNil) 728 err = cmd.Run(nil) 729 c.Assert(err, gc.ErrorMatches, "failed to initialize state") 730 c.Assert(called, gc.Equals, 1) 731 } 732 733 func (s *BootstrapSuite) TestInitializeStateMinSocketTimeout(c *gc.C) { 734 var called int 735 initializeState := func(_ names.UserTag, _ agent.ConfigSetter, envCfg *config.Config, hostedModelConfig map[string]interface{}, machineCfg agentbootstrap.BootstrapMachineConfig, dialOpts mongo.DialOpts, policy state.Policy) (_ *state.State, _ *state.Machine, resultErr error) { 736 called++ 737 c.Assert(dialOpts.Direct, jc.IsTrue) 738 c.Assert(dialOpts.SocketTimeout, gc.Equals, 1*time.Minute) 739 return nil, nil, errors.New("failed to initialize state") 740 } 741 742 envcfg, err := s.envcfg.Apply(map[string]interface{}{ 743 "bootstrap-timeout": "13", 744 }) 745 c.Assert(err, jc.ErrorIsNil) 746 b64yamlControllerModelConfig := b64yaml(envcfg.AllAttrs()).encode() 747 748 s.PatchValue(&agentInitializeState, initializeState) 749 _, cmd, err := s.initBootstrapCommand(c, nil, 750 "--model-config", b64yamlControllerModelConfig, 751 "--hosted-model-config", s.b64yamlHostedModelConfig, 752 "--instance-id", string(s.instanceId), 753 ) 754 c.Assert(err, jc.ErrorIsNil) 755 err = cmd.Run(nil) 756 c.Assert(err, gc.ErrorMatches, "failed to initialize state") 757 c.Assert(called, gc.Equals, 1) 758 } 759 760 func (s *BootstrapSuite) TestSystemIdentityWritten(c *gc.C) { 761 _, err := os.Stat(filepath.Join(s.dataDir, agent.SystemIdentity)) 762 c.Assert(err, jc.Satisfies, os.IsNotExist) 763 764 _, cmd, err := s.initBootstrapCommand(c, nil, 765 "--model-config", s.b64yamlControllerModelConfig, 766 "--hosted-model-config", s.b64yamlHostedModelConfig, 767 "--instance-id", string(s.instanceId), 768 ) 769 c.Assert(err, jc.ErrorIsNil) 770 err = cmd.Run(nil) 771 c.Assert(err, jc.ErrorIsNil) 772 773 data, err := ioutil.ReadFile(filepath.Join(s.dataDir, agent.SystemIdentity)) 774 c.Assert(err, jc.ErrorIsNil) 775 c.Assert(string(data), gc.Equals, "private-key") 776 } 777 778 func (s *BootstrapSuite) TestDownloadedToolsMetadata(c *gc.C) { 779 // Tools downloaded by cloud-init script. 780 s.testToolsMetadata(c, false) 781 } 782 783 func (s *BootstrapSuite) TestUploadedToolsMetadata(c *gc.C) { 784 // Tools uploaded over ssh. 785 s.writeDownloadedTools(c, &tools.Tools{ 786 Version: version.Binary{ 787 Number: jujuversion.Current, 788 Arch: arch.HostArch(), 789 Series: series.HostSeries(), 790 }, 791 URL: "file:///does/not/matter", 792 }) 793 s.testToolsMetadata(c, true) 794 } 795 796 func (s *BootstrapSuite) testToolsMetadata(c *gc.C, exploded bool) { 797 envtesting.RemoveFakeToolsMetadata(c, s.toolsStorage) 798 799 _, cmd, err := s.initBootstrapCommand(c, nil, 800 "--model-config", s.b64yamlControllerModelConfig, 801 "--hosted-model-config", s.b64yamlHostedModelConfig, 802 "--instance-id", string(s.instanceId), 803 ) 804 c.Assert(err, jc.ErrorIsNil) 805 err = cmd.Run(nil) 806 c.Assert(err, jc.ErrorIsNil) 807 808 // We don't write metadata at bootstrap anymore. 809 simplestreamsMetadata, err := envtools.ReadMetadata(s.toolsStorage, "released") 810 c.Assert(err, jc.ErrorIsNil) 811 c.Assert(simplestreamsMetadata, gc.HasLen, 0) 812 813 // The tools should have been added to tools storage, and 814 // exploded into each of the supported series of 815 // the same operating system if the tools were uploaded. 816 st, err := state.Open(testing.ModelTag, &mongo.MongoInfo{ 817 Info: mongo.Info{ 818 Addrs: []string{gitjujutesting.MgoServer.Addr()}, 819 CACert: testing.CACert, 820 }, 821 Password: testPassword, 822 }, mongo.DefaultDialOpts(), environs.NewStatePolicy()) 823 c.Assert(err, jc.ErrorIsNil) 824 defer st.Close() 825 expectedSeries := make(set.Strings) 826 if exploded { 827 for _, ser := range series.SupportedSeries() { 828 os, err := series.GetOSFromSeries(ser) 829 c.Assert(err, jc.ErrorIsNil) 830 hostos, err := series.GetOSFromSeries(series.HostSeries()) 831 c.Assert(err, jc.ErrorIsNil) 832 if os == hostos { 833 expectedSeries.Add(ser) 834 } 835 } 836 } else { 837 expectedSeries.Add(series.HostSeries()) 838 } 839 840 storage, err := st.ToolsStorage() 841 c.Assert(err, jc.ErrorIsNil) 842 defer storage.Close() 843 metadata, err := storage.AllMetadata() 844 c.Assert(err, jc.ErrorIsNil) 845 c.Assert(metadata, gc.HasLen, expectedSeries.Size()) 846 for _, m := range metadata { 847 v := version.MustParseBinary(m.Version) 848 c.Assert(expectedSeries.Contains(v.Series), jc.IsTrue) 849 } 850 } 851 852 const ( 853 indexContent = `{ 854 "index": { 855 "com.ubuntu.cloud:%v": { 856 "updated": "Fri, 17 Jul 2015 13:42:48 +1000", 857 "format": "products:1.0", 858 "datatype": "image-ids", 859 "cloudname": "custom", 860 "clouds": [ 861 { 862 "region": "%v", 863 "endpoint": "endpoint" 864 } 865 ], 866 "path": "streams/v1/products.json", 867 "products": [ 868 "com.ubuntu.cloud:server:14.04:%v" 869 ] 870 } 871 }, 872 "updated": "Fri, 17 Jul 2015 13:42:48 +1000", 873 "format": "index:1.0" 874 }` 875 876 productContent = `{ 877 "products": { 878 "com.ubuntu.cloud:server:14.04:%v": { 879 "version": "14.04", 880 "arch": "%v", 881 "versions": { 882 "20151707": { 883 "items": { 884 "%v": { 885 "id": "%v", 886 "root_store": "%v", 887 "virt": "%v", 888 "region": "%v", 889 "endpoint": "endpoint" 890 } 891 } 892 } 893 } 894 } 895 }, 896 "updated": "Fri, 17 Jul 2015 13:42:48 +1000", 897 "format": "products:1.0", 898 "content_id": "com.ubuntu.cloud:%v" 899 }` 900 ) 901 902 func writeTempFiles(c *gc.C, metadataDir string, expected []struct{ path, content string }) { 903 for _, pair := range expected { 904 path := filepath.Join(metadataDir, pair.path) 905 err := os.MkdirAll(filepath.Dir(path), 0755) 906 c.Assert(err, jc.ErrorIsNil) 907 err = ioutil.WriteFile(path, []byte(pair.content), 0644) 908 c.Assert(err, jc.ErrorIsNil) 909 } 910 } 911 912 func createImageMetadata(c *gc.C) (string, cloudimagemetadata.Metadata, []struct{ path, content string }) { 913 // setup data for this test 914 metadata := cloudimagemetadata.Metadata{ 915 MetadataAttributes: cloudimagemetadata.MetadataAttributes{ 916 Region: "region", 917 Series: "trusty", 918 Arch: "amd64", 919 VirtType: "virtType", 920 RootStorageType: "rootStore", 921 Source: "custom"}, 922 Priority: simplestreams.CUSTOM_CLOUD_DATA, 923 ImageId: "imageId"} 924 925 // setup files containing test's data 926 metadataDir := c.MkDir() 927 expected := []struct{ path, content string }{{ 928 path: "streams/v1/index.json", 929 content: fmt.Sprintf(indexContent, metadata.Source, metadata.Region, metadata.Arch), 930 }, { 931 path: "streams/v1/products.json", 932 content: fmt.Sprintf(productContent, metadata.Arch, metadata.Arch, metadata.ImageId, metadata.ImageId, metadata.RootStorageType, metadata.VirtType, metadata.Region, metadata.Source), 933 }, { 934 path: "wayward/file.txt", 935 content: "ghi", 936 }} 937 writeTempFiles(c, metadataDir, expected) 938 return metadataDir, metadata, expected 939 } 940 941 func assertWrittenToState(c *gc.C, metadata cloudimagemetadata.Metadata) { 942 st, err := state.Open(testing.ModelTag, &mongo.MongoInfo{ 943 Info: mongo.Info{ 944 Addrs: []string{gitjujutesting.MgoServer.Addr()}, 945 CACert: testing.CACert, 946 }, 947 Password: testPassword, 948 }, mongo.DefaultDialOpts(), environs.NewStatePolicy()) 949 c.Assert(err, jc.ErrorIsNil) 950 defer st.Close() 951 952 // find all image metadata in state 953 all, err := st.CloudImageMetadataStorage.FindMetadata(cloudimagemetadata.MetadataFilter{}) 954 c.Assert(err, jc.ErrorIsNil) 955 c.Assert(all, gc.DeepEquals, map[string][]cloudimagemetadata.Metadata{ 956 metadata.Source: []cloudimagemetadata.Metadata{metadata}, 957 }) 958 } 959 960 func (s *BootstrapSuite) TestStructuredImageMetadataStored(c *gc.C) { 961 dir, m, _ := createImageMetadata(c) 962 _, cmd, err := s.initBootstrapCommand( 963 c, nil, 964 "--model-config", s.b64yamlControllerModelConfig, 965 "--hosted-model-config", s.b64yamlHostedModelConfig, 966 "--instance-id", string(s.instanceId), 967 "--image-metadata", dir, 968 ) 969 c.Assert(err, jc.ErrorIsNil) 970 err = cmd.Run(nil) 971 c.Assert(err, jc.ErrorIsNil) 972 973 // This metadata should have also been written to state... 974 // m.Version would be deduced from m.Series 975 m.Version = "14.04" 976 assertWrittenToState(c, m) 977 978 } 979 980 func (s *BootstrapSuite) TestCustomDataSourceHasKey(c *gc.C) { 981 dir, _, _ := createImageMetadata(c) 982 _, cmd, err := s.initBootstrapCommand( 983 c, nil, 984 "--model-config", s.b64yamlControllerModelConfig, 985 "--hosted-model-config", s.b64yamlHostedModelConfig, 986 "--instance-id", string(s.instanceId), 987 "--image-metadata", dir, 988 ) 989 c.Assert(err, jc.ErrorIsNil) 990 991 called := false 992 s.PatchValue(&storeImageMetadataFromFiles, func(st *state.State, env environs.Environ, source simplestreams.DataSource) error { 993 called = true 994 // This data source does not require to contain signed data. 995 // However, it may still contain it. 996 // Since we will always try to read signed data first, 997 // we want to be able to try to read this signed data 998 // with a user provided public key. For this test, none is provided. 999 // Bugs #1542127, #1542131 1000 c.Assert(source.PublicSigningKey(), gc.Equals, "") 1001 return nil 1002 }) 1003 err = cmd.Run(nil) 1004 c.Assert(err, jc.ErrorIsNil) 1005 c.Assert(called, jc.IsTrue) 1006 } 1007 1008 func (s *BootstrapSuite) TestStructuredImageMetadataInvalidSeries(c *gc.C) { 1009 dir, _, _ := createImageMetadata(c) 1010 1011 msg := "my test error" 1012 s.PatchValue(&seriesFromVersion, func(string) (string, error) { 1013 return "", errors.New(msg) 1014 }) 1015 1016 _, cmd, err := s.initBootstrapCommand( 1017 c, nil, 1018 "--model-config", s.b64yamlControllerModelConfig, 1019 "--hosted-model-config", s.b64yamlHostedModelConfig, 1020 "--instance-id", string(s.instanceId), 1021 "--image-metadata", dir, 1022 ) 1023 c.Assert(err, jc.ErrorIsNil) 1024 err = cmd.Run(nil) 1025 c.Assert(err, gc.ErrorMatches, fmt.Sprintf(".*%v.*", msg)) 1026 } 1027 1028 func (s *BootstrapSuite) TestImageMetadata(c *gc.C) { 1029 metadataDir, _, expected := createImageMetadata(c) 1030 1031 var stor statetesting.MapStorage 1032 s.PatchValue(&newStateStorage, func(string, *mgo.Session) statestorage.Storage { 1033 return &stor 1034 }) 1035 1036 _, cmd, err := s.initBootstrapCommand( 1037 c, nil, 1038 "--model-config", s.b64yamlControllerModelConfig, 1039 "--hosted-model-config", s.b64yamlHostedModelConfig, 1040 "--instance-id", string(s.instanceId), 1041 "--image-metadata", metadataDir, 1042 ) 1043 c.Assert(err, jc.ErrorIsNil) 1044 err = cmd.Run(nil) 1045 c.Assert(err, jc.ErrorIsNil) 1046 1047 // The contents of the directory should have been added to 1048 // environment storage. 1049 for _, pair := range expected { 1050 r, length, err := stor.Get(pair.path) 1051 c.Assert(err, jc.ErrorIsNil) 1052 data, err := ioutil.ReadAll(r) 1053 r.Close() 1054 c.Assert(err, jc.ErrorIsNil) 1055 c.Assert(length, gc.Equals, int64(len(pair.content))) 1056 c.Assert(data, gc.HasLen, int(length)) 1057 c.Assert(string(data), gc.Equals, pair.content) 1058 } 1059 } 1060 1061 func (s *BootstrapSuite) makeTestEnv(c *gc.C) { 1062 attrs := dummy.SampleConfig().Merge( 1063 testing.Attrs{ 1064 "agent-version": jujuversion.Current.String(), 1065 "bootstrap-timeout": "123", 1066 }, 1067 ).Delete("admin-secret", "ca-private-key") 1068 cfg, err := config.New(config.NoDefaults, attrs) 1069 c.Assert(err, jc.ErrorIsNil) 1070 provider, err := environs.Provider(cfg.Type()) 1071 c.Assert(err, jc.ErrorIsNil) 1072 cfg, err = provider.BootstrapConfig(environs.BootstrapConfigParams{Config: cfg}) 1073 c.Assert(err, jc.ErrorIsNil) 1074 env, err := provider.PrepareForBootstrap(nullContext(), cfg) 1075 c.Assert(err, jc.ErrorIsNil) 1076 1077 s.PatchValue(&juju.JujuPublicKey, sstesting.SignedMetadataPublicKey) 1078 envtesting.MustUploadFakeTools(s.toolsStorage, cfg.AgentStream(), cfg.AgentStream()) 1079 inst, _, _, err := jujutesting.StartInstance(env, "0") 1080 c.Assert(err, jc.ErrorIsNil) 1081 s.instanceId = inst.Id() 1082 1083 addresses, err := inst.Addresses() 1084 c.Assert(err, jc.ErrorIsNil) 1085 addr, _ := network.SelectPublicAddress(addresses) 1086 s.bootstrapName = addr.Value 1087 s.envcfg = env.Config() 1088 s.b64yamlControllerModelConfig = b64yaml(s.envcfg.AllAttrs()).encode() 1089 1090 s.hostedModelUUID = utils.MustNewUUID().String() 1091 hostedModelConfigAttrs := map[string]interface{}{ 1092 "name": "hosted-model", 1093 "uuid": s.hostedModelUUID, 1094 } 1095 s.b64yamlHostedModelConfig = b64yaml(hostedModelConfigAttrs).encode() 1096 } 1097 1098 func nullContext() environs.BootstrapContext { 1099 ctx, _ := cmd.DefaultContext() 1100 ctx.Stdin = io.LimitReader(nil, 0) 1101 ctx.Stdout = ioutil.Discard 1102 ctx.Stderr = ioutil.Discard 1103 return modelcmd.BootstrapContext(ctx) 1104 } 1105 1106 type b64yaml map[string]interface{} 1107 1108 func (m b64yaml) encode() string { 1109 data, err := goyaml.Marshal(m) 1110 if err != nil { 1111 panic(err) 1112 } 1113 return base64.StdEncoding.EncodeToString(data) 1114 } 1115 1116 func (s *BootstrapSuite) TestDefaultStoragePools(c *gc.C) { 1117 _, cmd, err := s.initBootstrapCommand( 1118 c, nil, "--model-config", s.b64yamlControllerModelConfig, 1119 "--hosted-model-config", s.b64yamlHostedModelConfig, 1120 "--instance-id", string(s.instanceId), 1121 ) 1122 c.Assert(err, jc.ErrorIsNil) 1123 err = cmd.Run(nil) 1124 c.Assert(err, jc.ErrorIsNil) 1125 1126 st, err := state.Open(testing.ModelTag, &mongo.MongoInfo{ 1127 Info: mongo.Info{ 1128 Addrs: []string{gitjujutesting.MgoServer.Addr()}, 1129 CACert: testing.CACert, 1130 }, 1131 Password: testPassword, 1132 }, mongo.DefaultDialOpts(), environs.NewStatePolicy()) 1133 c.Assert(err, jc.ErrorIsNil) 1134 defer st.Close() 1135 1136 settings := state.NewStateSettings(st) 1137 pm := poolmanager.New(settings) 1138 for _, p := range []string{"ebs-ssd"} { 1139 _, err = pm.Get(p) 1140 c.Assert(err, jc.ErrorIsNil) 1141 } 1142 }