github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/juju/testing/conn.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package testing 5 6 import ( 7 "fmt" 8 "io/ioutil" 9 "os" 10 "path/filepath" 11 "strings" 12 "time" 13 14 "github.com/juju/errors" 15 "github.com/juju/names" 16 gitjujutesting "github.com/juju/testing" 17 jc "github.com/juju/testing/checkers" 18 "github.com/juju/utils" 19 gc "gopkg.in/check.v1" 20 "gopkg.in/juju/charm.v6-unstable" 21 "gopkg.in/juju/charmrepo.v1" 22 goyaml "gopkg.in/yaml.v1" 23 24 "github.com/juju/juju/agent" 25 "github.com/juju/juju/api" 26 "github.com/juju/juju/cmd/envcmd" 27 "github.com/juju/juju/environs" 28 "github.com/juju/juju/environs/bootstrap" 29 "github.com/juju/juju/environs/config" 30 "github.com/juju/juju/environs/configstore" 31 "github.com/juju/juju/environs/filestorage" 32 "github.com/juju/juju/environs/storage" 33 envtesting "github.com/juju/juju/environs/testing" 34 "github.com/juju/juju/environs/tools" 35 "github.com/juju/juju/juju" 36 "github.com/juju/juju/juju/osenv" 37 "github.com/juju/juju/mongo" 38 "github.com/juju/juju/provider/dummy" 39 "github.com/juju/juju/state" 40 statestorage "github.com/juju/juju/state/storage" 41 "github.com/juju/juju/state/toolstorage" 42 "github.com/juju/juju/testcharms" 43 "github.com/juju/juju/testing" 44 "github.com/juju/juju/testing/factory" 45 "github.com/juju/juju/version" 46 ) 47 48 // JujuConnSuite provides a freshly bootstrapped juju.Conn 49 // for each test. It also includes testing.BaseSuite. 50 // 51 // It also sets up RootDir to point to a directory hierarchy 52 // mirroring the intended juju directory structure, including 53 // the following: 54 // RootDir/home/ubuntu/.juju/environments.yaml 55 // The dummy environments.yaml file, holding 56 // a default environment named "dummyenv" 57 // which uses the "dummy" environment type. 58 // RootDir/var/lib/juju 59 // An empty directory returned as DataDir - the 60 // root of the juju data storage space. 61 // $HOME is set to point to RootDir/home/ubuntu. 62 type JujuConnSuite struct { 63 // TODO: JujuConnSuite should not be concerned both with JUJU_HOME and with 64 // /var/lib/juju: the use cases are completely non-overlapping, and any tests that 65 // really do need both to exist ought to be embedding distinct fixtures for the 66 // distinct environments. 67 gitjujutesting.MgoSuite 68 testing.FakeJujuHomeSuite 69 envtesting.ToolsFixture 70 71 DefaultToolsStorageDir string 72 DefaultToolsStorage storage.Storage 73 74 State *state.State 75 Environ environs.Environ 76 APIState api.Connection 77 apiStates []api.Connection // additional api.Connections to close on teardown 78 ConfigStore configstore.Storage 79 BackingState *state.State // The State being used by the API server 80 RootDir string // The faked-up root directory. 81 LogDir string 82 oldHome string 83 oldJujuHome string 84 DummyConfig testing.Attrs 85 Factory *factory.Factory 86 } 87 88 const AdminSecret = "dummy-secret" 89 90 func (s *JujuConnSuite) SetUpSuite(c *gc.C) { 91 s.MgoSuite.SetUpSuite(c) 92 s.FakeJujuHomeSuite.SetUpSuite(c) 93 } 94 95 func (s *JujuConnSuite) TearDownSuite(c *gc.C) { 96 s.FakeJujuHomeSuite.TearDownSuite(c) 97 s.MgoSuite.TearDownSuite(c) 98 } 99 100 func (s *JujuConnSuite) SetUpTest(c *gc.C) { 101 s.MgoSuite.SetUpTest(c) 102 s.FakeJujuHomeSuite.SetUpTest(c) 103 s.ToolsFixture.SetUpTest(c) 104 s.PatchValue(&configstore.DefaultAdminUsername, dummy.AdminUserTag().Name()) 105 s.setUpConn(c) 106 s.Factory = factory.NewFactory(s.State) 107 } 108 109 func (s *JujuConnSuite) TearDownTest(c *gc.C) { 110 s.tearDownConn(c) 111 s.ToolsFixture.TearDownTest(c) 112 s.FakeJujuHomeSuite.TearDownTest(c) 113 s.MgoSuite.TearDownTest(c) 114 } 115 116 // Reset returns environment state to that which existed at the start of 117 // the test. 118 func (s *JujuConnSuite) Reset(c *gc.C) { 119 s.tearDownConn(c) 120 s.setUpConn(c) 121 } 122 123 func (s *JujuConnSuite) AdminUserTag(c *gc.C) names.UserTag { 124 env, err := s.State.StateServerEnvironment() 125 c.Assert(err, jc.ErrorIsNil) 126 return env.Owner() 127 } 128 129 func (s *JujuConnSuite) MongoInfo(c *gc.C) *mongo.MongoInfo { 130 info := s.State.MongoConnectionInfo() 131 info.Password = "dummy-secret" 132 return info 133 } 134 135 func (s *JujuConnSuite) APIInfo(c *gc.C) *api.Info { 136 apiInfo, err := environs.APIInfo(s.Environ) 137 c.Assert(err, jc.ErrorIsNil) 138 apiInfo.Tag = s.AdminUserTag(c) 139 apiInfo.Password = "dummy-secret" 140 apiInfo.EnvironTag = s.State.EnvironTag() 141 return apiInfo 142 } 143 144 // openAPIAs opens the API and ensures that the api.Connection returned will be 145 // closed during the test teardown by using a cleanup function. 146 func (s *JujuConnSuite) openAPIAs(c *gc.C, tag names.Tag, password, nonce string) api.Connection { 147 apiInfo := s.APIInfo(c) 148 apiInfo.Tag = tag 149 apiInfo.Password = password 150 apiInfo.Nonce = nonce 151 apiState, err := api.Open(apiInfo, api.DialOpts{}) 152 c.Assert(err, jc.ErrorIsNil) 153 c.Assert(apiState, gc.NotNil) 154 s.apiStates = append(s.apiStates, apiState) 155 return apiState 156 } 157 158 // OpenAPIAs opens the API using the given identity tag and password for 159 // authentication. The returned api.Connection should not be closed by the caller 160 // as a cleanup function has been registered to do that. 161 func (s *JujuConnSuite) OpenAPIAs(c *gc.C, tag names.Tag, password string) api.Connection { 162 return s.openAPIAs(c, tag, password, "") 163 } 164 165 // OpenAPIAsMachine opens the API using the given machine tag, password and 166 // nonce for authentication. The returned api.Connection should not be closed by 167 // the caller as a cleanup function has been registered to do that. 168 func (s *JujuConnSuite) OpenAPIAsMachine(c *gc.C, tag names.Tag, password, nonce string) api.Connection { 169 return s.openAPIAs(c, tag, password, nonce) 170 } 171 172 // OpenAPIAsNewMachine creates a new machine entry that lives in system state, 173 // and then uses that to open the API. The returned api.Connection should not be 174 // closed by the caller as a cleanup function has been registered to do that. 175 // The machine will run the supplied jobs; if none are given, JobHostUnits is assumed. 176 func (s *JujuConnSuite) OpenAPIAsNewMachine(c *gc.C, jobs ...state.MachineJob) (api.Connection, *state.Machine) { 177 if len(jobs) == 0 { 178 jobs = []state.MachineJob{state.JobHostUnits} 179 } 180 machine, err := s.State.AddMachine("quantal", jobs...) 181 c.Assert(err, jc.ErrorIsNil) 182 password, err := utils.RandomPassword() 183 c.Assert(err, jc.ErrorIsNil) 184 err = machine.SetPassword(password) 185 c.Assert(err, jc.ErrorIsNil) 186 err = machine.SetProvisioned("foo", "fake_nonce", nil) 187 c.Assert(err, jc.ErrorIsNil) 188 return s.openAPIAs(c, machine.Tag(), password, "fake_nonce"), machine 189 } 190 191 func PreferredDefaultVersions(conf *config.Config, template version.Binary) []version.Binary { 192 prefVersion := template 193 prefVersion.Series = config.PreferredSeries(conf) 194 defaultVersion := template 195 if prefVersion.Series != testing.FakeDefaultSeries { 196 defaultVersion.Series = testing.FakeDefaultSeries 197 } 198 return []version.Binary{prefVersion, defaultVersion} 199 } 200 201 func (s *JujuConnSuite) setUpConn(c *gc.C) { 202 if s.RootDir != "" { 203 panic("JujuConnSuite.setUpConn without teardown") 204 } 205 s.RootDir = c.MkDir() 206 s.oldHome = utils.Home() 207 home := filepath.Join(s.RootDir, "/home/ubuntu") 208 err := os.MkdirAll(home, 0777) 209 c.Assert(err, jc.ErrorIsNil) 210 utils.SetHome(home) 211 s.oldJujuHome = osenv.SetJujuHome(filepath.Join(home, ".juju")) 212 err = os.Mkdir(osenv.JujuHome(), 0777) 213 c.Assert(err, jc.ErrorIsNil) 214 215 err = os.MkdirAll(s.DataDir(), 0777) 216 c.Assert(err, jc.ErrorIsNil) 217 s.PatchEnvironment(osenv.JujuEnvEnvKey, "") 218 219 // TODO(rog) remove these files and add them only when 220 // the tests specifically need them (in cmd/juju for example) 221 s.writeSampleConfig(c, osenv.JujuHomePath("environments.yaml")) 222 223 err = ioutil.WriteFile(osenv.JujuHomePath("dummyenv-cert.pem"), []byte(testing.CACert), 0666) 224 c.Assert(err, jc.ErrorIsNil) 225 226 err = ioutil.WriteFile(osenv.JujuHomePath("dummyenv-private-key.pem"), []byte(testing.CAKey), 0600) 227 c.Assert(err, jc.ErrorIsNil) 228 229 store, err := configstore.Default() 230 c.Assert(err, jc.ErrorIsNil) 231 s.ConfigStore = store 232 233 ctx := testing.Context(c) 234 environ, err := environs.PrepareFromName("dummyenv", envcmd.BootstrapContext(ctx), s.ConfigStore) 235 c.Assert(err, jc.ErrorIsNil) 236 // sanity check we've got the correct environment. 237 c.Assert(environ.Config().Name(), gc.Equals, "dummyenv") 238 s.PatchValue(&dummy.DataDir, s.DataDir()) 239 s.LogDir = c.MkDir() 240 s.PatchValue(&dummy.LogDir, s.LogDir) 241 242 versions := PreferredDefaultVersions(environ.Config(), version.Binary{Number: version.Current.Number, Series: "precise", Arch: "amd64"}) 243 versions = append(versions, version.Current) 244 245 // Upload tools for both preferred and fake default series 246 s.DefaultToolsStorageDir = c.MkDir() 247 s.PatchValue(&tools.DefaultBaseURL, s.DefaultToolsStorageDir) 248 stor, err := filestorage.NewFileStorageWriter(s.DefaultToolsStorageDir) 249 c.Assert(err, jc.ErrorIsNil) 250 // Upload tools to both release and devel streams since config will dictate that we 251 // end up looking in both places. 252 envtesting.AssertUploadFakeToolsVersions(c, stor, "released", "released", versions...) 253 envtesting.AssertUploadFakeToolsVersions(c, stor, "devel", "devel", versions...) 254 s.DefaultToolsStorage = stor 255 256 err = bootstrap.Bootstrap(envcmd.BootstrapContext(ctx), environ, bootstrap.BootstrapParams{}) 257 c.Assert(err, jc.ErrorIsNil) 258 259 s.BackingState = environ.(GetStater).GetStateInAPIServer() 260 261 s.State, err = newState(environ, s.BackingState.MongoConnectionInfo()) 262 c.Assert(err, jc.ErrorIsNil) 263 264 s.APIState, err = juju.NewAPIState(s.AdminUserTag(c), environ, api.DialOpts{}) 265 c.Assert(err, jc.ErrorIsNil) 266 267 err = s.State.SetAPIHostPorts(s.APIState.APIHostPorts()) 268 c.Assert(err, jc.ErrorIsNil) 269 270 // Make sure the config store has the api endpoint address set 271 info, err := s.ConfigStore.ReadInfo("dummyenv") 272 c.Assert(err, jc.ErrorIsNil) 273 endpoint := info.APIEndpoint() 274 endpoint.Addresses = []string{s.APIState.APIHostPorts()[0][0].String()} 275 info.SetAPIEndpoint(endpoint) 276 err = info.Write() 277 c.Assert(err, jc.ErrorIsNil) 278 279 // Make sure the jenv file has the local host ports. 280 c.Logf("jenv host ports: %#v", s.APIState.APIHostPorts()) 281 282 s.Environ = environ 283 284 // Insert expected values... 285 servingInfo := state.StateServingInfo{ 286 PrivateKey: testing.ServerKey, 287 Cert: testing.ServerCert, 288 CAPrivateKey: testing.CAKey, 289 SharedSecret: "really, really secret", 290 APIPort: 4321, 291 StatePort: 1234, 292 } 293 s.State.SetStateServingInfo(servingInfo) 294 } 295 296 // AddToolsToState adds tools to tools storage. 297 func (s *JujuConnSuite) AddToolsToState(c *gc.C, versions ...version.Binary) { 298 stor, err := s.State.ToolsStorage() 299 c.Assert(err, jc.ErrorIsNil) 300 defer stor.Close() 301 for _, v := range versions { 302 content := v.String() 303 hash := fmt.Sprintf("sha256(%s)", content) 304 err := stor.AddTools(strings.NewReader(content), toolstorage.Metadata{ 305 Version: v, 306 Size: int64(len(content)), 307 SHA256: hash, 308 }) 309 c.Assert(err, jc.ErrorIsNil) 310 } 311 } 312 313 // AddDefaultToolsToState adds tools to tools storage for 314 // {Number: version.Current.Number, Arch: amd64}, for the 315 // "precise" series and the environment's preferred series. 316 // The preferred series is default-series if specified, 317 // otherwise the latest LTS. 318 func (s *JujuConnSuite) AddDefaultToolsToState(c *gc.C) { 319 preferredVersion := version.Current 320 preferredVersion.Arch = "amd64" 321 versions := PreferredDefaultVersions(s.Environ.Config(), preferredVersion) 322 versions = append(versions, version.Current) 323 s.AddToolsToState(c, versions...) 324 } 325 326 var redialStrategy = utils.AttemptStrategy{ 327 Total: 60 * time.Second, 328 Delay: 250 * time.Millisecond, 329 } 330 331 // newState returns a new State that uses the given environment. 332 // The environment must have already been bootstrapped. 333 func newState(environ environs.Environ, mongoInfo *mongo.MongoInfo) (*state.State, error) { 334 config := environ.Config() 335 password := config.AdminSecret() 336 if password == "" { 337 return nil, fmt.Errorf("cannot connect without admin-secret") 338 } 339 environUUID, ok := config.UUID() 340 if !ok { 341 return nil, fmt.Errorf("cannot connect without environment UUID") 342 } 343 environTag := names.NewEnvironTag(environUUID) 344 345 mongoInfo.Password = password 346 opts := mongo.DefaultDialOpts() 347 st, err := state.Open(environTag, mongoInfo, opts, environs.NewStatePolicy()) 348 if errors.IsUnauthorized(errors.Cause(err)) { 349 // We try for a while because we might succeed in 350 // connecting to mongo before the state has been 351 // initialized and the initial password set. 352 for a := redialStrategy.Start(); a.Next(); { 353 st, err = state.Open(environTag, mongoInfo, opts, environs.NewStatePolicy()) 354 if !errors.IsUnauthorized(errors.Cause(err)) { 355 break 356 } 357 } 358 if err != nil { 359 return nil, err 360 } 361 } else if err != nil { 362 return nil, err 363 } 364 if err := updateSecrets(environ, st); err != nil { 365 st.Close() 366 return nil, fmt.Errorf("unable to push secrets: %v", err) 367 } 368 return st, nil 369 } 370 371 func updateSecrets(env environs.Environ, st *state.State) error { 372 secrets, err := env.Provider().SecretAttrs(env.Config()) 373 if err != nil { 374 return err 375 } 376 cfg, err := st.EnvironConfig() 377 if err != nil { 378 return err 379 } 380 secretAttrs := make(map[string]interface{}) 381 attrs := cfg.AllAttrs() 382 for k, v := range secrets { 383 if _, exists := attrs[k]; exists { 384 // Environment already has secrets. Won't send again. 385 return nil 386 } else { 387 secretAttrs[k] = v 388 } 389 } 390 return st.UpdateEnvironConfig(secretAttrs, nil, nil) 391 } 392 393 // PutCharm uploads the given charm to provider storage, and adds a 394 // state.Charm to the state. The charm is not uploaded if a charm with 395 // the same URL already exists in the state. 396 // If bumpRevision is true, the charm must be a local directory, 397 // and the revision number will be incremented before pushing. 398 func PutCharm(st *state.State, curl *charm.URL, repo charmrepo.Interface, bumpRevision bool) (*state.Charm, error) { 399 if curl.Revision == -1 { 400 rev, err := charmrepo.Latest(repo, curl) 401 if err != nil { 402 return nil, fmt.Errorf("cannot get latest charm revision: %v", err) 403 } 404 curl = curl.WithRevision(rev) 405 } 406 ch, err := repo.Get(curl) 407 if err != nil { 408 return nil, fmt.Errorf("cannot get charm: %v", err) 409 } 410 if bumpRevision { 411 chd, ok := ch.(*charm.CharmDir) 412 if !ok { 413 return nil, fmt.Errorf("cannot increment revision of charm %q: not a directory", curl) 414 } 415 if err = chd.SetDiskRevision(chd.Revision() + 1); err != nil { 416 return nil, fmt.Errorf("cannot increment revision of charm %q: %v", curl, err) 417 } 418 curl = curl.WithRevision(chd.Revision()) 419 } 420 if sch, err := st.Charm(curl); err == nil { 421 return sch, nil 422 } 423 return addCharm(st, curl, ch) 424 } 425 426 func addCharm(st *state.State, curl *charm.URL, ch charm.Charm) (*state.Charm, error) { 427 var f *os.File 428 name := charm.Quote(curl.String()) 429 switch ch := ch.(type) { 430 case *charm.CharmDir: 431 var err error 432 if f, err = ioutil.TempFile("", name); err != nil { 433 return nil, err 434 } 435 defer os.Remove(f.Name()) 436 defer f.Close() 437 err = ch.ArchiveTo(f) 438 if err != nil { 439 return nil, fmt.Errorf("cannot bundle charm: %v", err) 440 } 441 if _, err := f.Seek(0, 0); err != nil { 442 return nil, err 443 } 444 case *charm.CharmArchive: 445 var err error 446 if f, err = os.Open(ch.Path); err != nil { 447 return nil, fmt.Errorf("cannot read charm bundle: %v", err) 448 } 449 defer f.Close() 450 default: 451 return nil, fmt.Errorf("unknown charm type %T", ch) 452 } 453 digest, size, err := utils.ReadSHA256(f) 454 if err != nil { 455 return nil, err 456 } 457 if _, err := f.Seek(0, 0); err != nil { 458 return nil, err 459 } 460 461 stor := statestorage.NewStorage(st.EnvironUUID(), st.MongoSession()) 462 storagePath := fmt.Sprintf("/charms/%s-%s", curl.String(), digest) 463 if err := stor.Put(storagePath, f, size); err != nil { 464 return nil, fmt.Errorf("cannot put charm: %v", err) 465 } 466 sch, err := st.AddCharm(ch, curl, storagePath, digest) 467 if err != nil { 468 return nil, fmt.Errorf("cannot add charm: %v", err) 469 } 470 return sch, nil 471 } 472 473 func (s *JujuConnSuite) writeSampleConfig(c *gc.C, path string) { 474 if s.DummyConfig == nil { 475 s.DummyConfig = dummy.SampleConfig() 476 } 477 attrs := s.DummyConfig.Merge(testing.Attrs{ 478 "admin-secret": AdminSecret, 479 "agent-version": version.Current.Number.String(), 480 }).Delete("name") 481 whole := map[string]interface{}{ 482 "environments": map[string]interface{}{ 483 "dummyenv": attrs, 484 }, 485 } 486 data, err := goyaml.Marshal(whole) 487 c.Assert(err, jc.ErrorIsNil) 488 s.WriteConfig(string(data)) 489 } 490 491 type GetStater interface { 492 GetStateInAPIServer() *state.State 493 } 494 495 func (s *JujuConnSuite) tearDownConn(c *gc.C) { 496 testServer := gitjujutesting.MgoServer.Addr() 497 serverAlive := testServer != "" 498 499 // Close any api connections we know about first. 500 for _, st := range s.apiStates { 501 err := st.Close() 502 if serverAlive { 503 c.Check(err, jc.ErrorIsNil) 504 } 505 } 506 s.apiStates = nil 507 if s.APIState != nil { 508 err := s.APIState.Close() 509 s.APIState = nil 510 if serverAlive { 511 c.Check(err, gc.IsNil, 512 gc.Commentf("closing api state failed\n%s\n", errors.ErrorStack(err)), 513 ) 514 } 515 } 516 // Close state. 517 if s.State != nil { 518 err := s.State.Close() 519 if serverAlive { 520 // This happens way too often with failing tests, 521 // so add some context in case of an error. 522 c.Check(err, gc.IsNil, 523 gc.Commentf("closing state failed\n%s\n", errors.ErrorStack(err)), 524 ) 525 } 526 s.State = nil 527 } 528 529 dummy.Reset() 530 utils.SetHome(s.oldHome) 531 osenv.SetJujuHome(s.oldJujuHome) 532 s.oldHome = "" 533 s.RootDir = "" 534 } 535 536 func (s *JujuConnSuite) DataDir() string { 537 if s.RootDir == "" { 538 panic("DataDir called out of test context") 539 } 540 return filepath.Join(s.RootDir, "/var/lib/juju") 541 } 542 543 func (s *JujuConnSuite) ConfDir() string { 544 if s.RootDir == "" { 545 panic("DataDir called out of test context") 546 } 547 return filepath.Join(s.RootDir, "/etc/juju") 548 } 549 550 // WriteConfig writes a juju config file to the "home" directory. 551 func (s *JujuConnSuite) WriteConfig(configData string) { 552 if s.RootDir == "" { 553 panic("SetUpTest has not been called; will not overwrite $JUJU_HOME/environments.yaml") 554 } 555 path := osenv.JujuHomePath("environments.yaml") 556 err := ioutil.WriteFile(path, []byte(configData), 0600) 557 if err != nil { 558 panic(err) 559 } 560 } 561 562 func (s *JujuConnSuite) AddTestingCharm(c *gc.C, name string) *state.Charm { 563 ch := testcharms.Repo.CharmDir(name) 564 ident := fmt.Sprintf("%s-%d", ch.Meta().Name, ch.Revision()) 565 curl := charm.MustParseURL("local:quantal/" + ident) 566 repo, err := charmrepo.InferRepository( 567 curl.Reference(), 568 charmrepo.NewCharmStoreParams{}, 569 testcharms.Repo.Path()) 570 c.Assert(err, jc.ErrorIsNil) 571 sch, err := PutCharm(s.State, curl, repo, false) 572 c.Assert(err, jc.ErrorIsNil) 573 return sch 574 } 575 576 func (s *JujuConnSuite) AddTestingService(c *gc.C, name string, ch *state.Charm) *state.Service { 577 return s.AddTestingServiceWithNetworks(c, name, ch, nil) 578 } 579 580 func (s *JujuConnSuite) AddTestingServiceWithStorage(c *gc.C, name string, ch *state.Charm, storage map[string]state.StorageConstraints) *state.Service { 581 owner := s.AdminUserTag(c).String() 582 service, err := s.State.AddService(name, owner, ch, nil, storage) 583 c.Assert(err, jc.ErrorIsNil) 584 return service 585 } 586 587 func (s *JujuConnSuite) AddTestingServiceWithNetworks(c *gc.C, name string, ch *state.Charm, networks []string) *state.Service { 588 c.Assert(s.State, gc.NotNil) 589 owner := s.AdminUserTag(c).String() 590 service, err := s.State.AddService(name, owner, ch, networks, nil) 591 c.Assert(err, jc.ErrorIsNil) 592 return service 593 } 594 595 func (s *JujuConnSuite) AgentConfigForTag(c *gc.C, tag names.Tag) agent.ConfigSetter { 596 password, err := utils.RandomPassword() 597 c.Assert(err, jc.ErrorIsNil) 598 paths := agent.DefaultPaths 599 paths.DataDir = s.DataDir() 600 config, err := agent.NewAgentConfig( 601 agent.AgentConfigParams{ 602 Paths: paths, 603 Tag: tag, 604 UpgradedToVersion: version.Current.Number, 605 Password: password, 606 Nonce: "nonce", 607 StateAddresses: s.MongoInfo(c).Addrs, 608 APIAddresses: s.APIInfo(c).Addrs, 609 CACert: testing.CACert, 610 Environment: s.State.EnvironTag(), 611 }) 612 c.Assert(err, jc.ErrorIsNil) 613 return config 614 } 615 616 // AssertConfigParameterUpdated updates environment parameter and 617 // asserts that no errors were encountered 618 func (s *JujuConnSuite) AssertConfigParameterUpdated(c *gc.C, key string, value interface{}) { 619 err := s.BackingState.UpdateEnvironConfig(map[string]interface{}{key: value}, nil, nil) 620 c.Assert(err, jc.ErrorIsNil) 621 }