github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/provider/lxd/testing_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package lxd 5 6 import ( 7 stdcontext "context" 8 "net" 9 "os" 10 "strconv" 11 "time" 12 13 lxdclient "github.com/canonical/lxd/client" 14 "github.com/canonical/lxd/shared/api" 15 "github.com/juju/clock" 16 "github.com/juju/errors" 17 jujutesting "github.com/juju/testing" 18 jc "github.com/juju/testing/checkers" 19 "github.com/juju/version/v2" 20 gc "gopkg.in/check.v1" 21 22 "github.com/juju/juju/cloud" 23 "github.com/juju/juju/cloudconfig/instancecfg" 24 "github.com/juju/juju/cloudconfig/providerinit" 25 "github.com/juju/juju/container/lxd" 26 containerlxd "github.com/juju/juju/container/lxd" 27 "github.com/juju/juju/core/arch" 28 corebase "github.com/juju/juju/core/base" 29 "github.com/juju/juju/core/constraints" 30 "github.com/juju/juju/core/instance" 31 "github.com/juju/juju/core/network" 32 "github.com/juju/juju/core/network/firewall" 33 "github.com/juju/juju/environs" 34 environscloudspec "github.com/juju/juju/environs/cloudspec" 35 "github.com/juju/juju/environs/config" 36 "github.com/juju/juju/environs/context" 37 "github.com/juju/juju/environs/instances" 38 "github.com/juju/juju/environs/tags" 39 "github.com/juju/juju/testing" 40 coretools "github.com/juju/juju/tools" 41 jujuversion "github.com/juju/juju/version" 42 ) 43 44 // Ensure LXD provider supports the expected interfaces. 45 var ( 46 _ config.ConfigSchemaSource = (*environProvider)(nil) 47 ) 48 49 // These are stub config values for use in tests. 50 var ( 51 ConfigAttrs = testing.FakeConfig().Merge(testing.Attrs{ 52 "type": "lxd", 53 "uuid": "2d02eeac-9dbb-11e4-89d3-123b93f75cba", 54 }) 55 ) 56 57 // We test these here since they are not exported. 58 var ( 59 _ environs.Environ = (*environ)(nil) 60 _ instances.Instance = (*environInstance)(nil) 61 ) 62 63 type BaseSuiteUnpatched struct { 64 testing.BaseSuite 65 66 osPathOrig string 67 68 Config *config.Config 69 EnvConfig *environConfig 70 Provider *environProvider 71 Env *environ 72 73 Addresses network.ProviderAddresses 74 Instance *environInstance 75 Container *lxd.Container 76 InstName string 77 HWC *instance.HardwareCharacteristics 78 Metadata map[string]string 79 StartInstArgs environs.StartInstanceParams 80 81 Rules firewall.IngressRules 82 EndpointAddrs []string 83 InterfaceAddr string 84 InterfaceAddrs []net.Addr 85 } 86 87 func (s *BaseSuiteUnpatched) SetUpSuite(c *gc.C) { 88 s.osPathOrig = os.Getenv("PATH") 89 if s.osPathOrig == "" { 90 // TODO(ericsnow) This shouldn't happen. However, an undiagnosed 91 // bug in testing.BaseSuite is causing $PATH to remain unset 92 // sometimes. Once that is cleared up this special-case can go 93 // away. 94 s.osPathOrig = 95 "/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin" 96 } 97 s.BaseSuite.SetUpSuite(c) 98 } 99 100 func (s *BaseSuiteUnpatched) SetUpTest(c *gc.C) { 101 s.BaseSuite.SetUpTest(c) 102 103 s.initProvider(c) 104 s.initEnv(c) 105 s.initInst(c) 106 s.initNet(c) 107 } 108 109 func (s *BaseSuiteUnpatched) initProvider(c *gc.C) { 110 s.Provider = &environProvider{} 111 s.EndpointAddrs = []string{"1.2.3.4"} 112 s.InterfaceAddr = "1.2.3.4" 113 s.InterfaceAddrs = []net.Addr{ 114 &net.IPNet{IP: net.ParseIP("127.0.0.1")}, 115 &net.IPNet{IP: net.ParseIP("1.2.3.4")}, 116 } 117 } 118 119 func (s *BaseSuiteUnpatched) initEnv(c *gc.C) { 120 certCred := cloud.NewCredential(cloud.CertificateAuthType, map[string]string{ 121 "client-cert": testing.CACert, 122 "client-key": testing.CAKey, 123 "server-cert": testing.ServerCert, 124 }) 125 s.Env = &environ{ 126 cloud: environscloudspec.CloudSpec{ 127 Name: "localhost", 128 Type: "lxd", 129 Credential: &certCred, 130 }, 131 provider: s.Provider, 132 name: "lxd", 133 } 134 cfg := s.NewConfig(c, nil) 135 s.setConfig(c, cfg) 136 } 137 138 func (s *BaseSuiteUnpatched) Prefix() string { 139 return s.Env.namespace.Prefix() 140 } 141 142 func (s *BaseSuiteUnpatched) initInst(c *gc.C) { 143 tools := []*coretools.Tools{ 144 { 145 Version: version.Binary{Arch: arch.AMD64, Release: "ubuntu"}, 146 URL: "https://example.org/amd", 147 }, 148 { 149 Version: version.Binary{Arch: arch.ARM64, Release: "ubuntu"}, 150 URL: "https://example.org/arm", 151 }, 152 } 153 154 cons := constraints.Value{} 155 156 instanceConfig, err := instancecfg.NewBootstrapInstanceConfig(testing.FakeControllerConfig(), cons, cons, 157 jujuversion.DefaultSupportedLTSBase(), "", nil) 158 c.Assert(err, jc.ErrorIsNil) 159 160 err = instanceConfig.SetTools(coretools.List{ 161 tools[0], 162 }) 163 c.Assert(err, jc.ErrorIsNil) 164 instanceConfig.AuthorizedKeys = s.Config.AuthorizedKeys() 165 166 userData, err := providerinit.ComposeUserData(instanceConfig, nil, lxdRenderer{}) 167 c.Assert(err, jc.ErrorIsNil) 168 169 var archName = arch.ARM64 170 var numCores uint64 = 1 171 var memoryMB uint64 = 3750 172 s.HWC = &instance.HardwareCharacteristics{ 173 Arch: &archName, 174 CpuCores: &numCores, 175 Mem: &memoryMB, 176 } 177 178 s.Metadata = map[string]string{ 179 containerlxd.UserNamespacePrefix + tags.JujuIsController: "true", 180 containerlxd.UserNamespacePrefix + tags.JujuController: testing.ControllerTag.Id(), 181 containerlxd.JujuModelKey: s.Config.UUID(), 182 containerlxd.UserDataKey: string(userData), 183 "limits.cpu": "1", 184 "limits.memory": strconv.Itoa(3750 * 1024 * 1024), 185 } 186 s.Addresses = network.ProviderAddresses{ 187 network.NewMachineAddress("10.0.0.1", network.WithScope(network.ScopeCloudLocal)).AsProviderAddress(), 188 } 189 190 // NOTE: the instance ids used throughout this package are not at all 191 // representative of what they would normally be. They would normally 192 // determined by the instance namespace and the machine id. 193 s.Instance = s.NewInstance(c, "spam") 194 s.Container = s.Instance.container 195 s.InstName, err = s.Env.namespace.Hostname("42") 196 c.Assert(err, jc.ErrorIsNil) 197 198 s.StartInstArgs = environs.StartInstanceParams{ 199 ControllerUUID: instanceConfig.ControllerConfig.ControllerUUID(), 200 InstanceConfig: instanceConfig, 201 Tools: tools, 202 Constraints: cons, 203 } 204 } 205 206 func (s *BaseSuiteUnpatched) initNet(c *gc.C) { 207 s.Rules = firewall.IngressRules{ 208 firewall.NewIngressRule(network.MustParsePortRange("80/tcp")), 209 } 210 } 211 212 func (s *BaseSuiteUnpatched) setConfig(c *gc.C, cfg *config.Config) { 213 s.Config = cfg 214 ecfg, err := newValidConfig(cfg) 215 c.Assert(err, jc.ErrorIsNil) 216 s.EnvConfig = ecfg 217 uuid := cfg.UUID() 218 s.Env.uuid = uuid 219 s.Env.ecfgUnlocked = s.EnvConfig 220 namespace, err := instance.NewNamespace(uuid) 221 c.Assert(err, jc.ErrorIsNil) 222 s.Env.namespace = namespace 223 } 224 225 func (s *BaseSuiteUnpatched) NewConfig(c *gc.C, updates testing.Attrs) *config.Config { 226 if updates == nil { 227 updates = make(testing.Attrs) 228 } 229 var err error 230 cfg := testing.ModelConfig(c) 231 cfg, err = cfg.Apply(ConfigAttrs) 232 c.Assert(err, jc.ErrorIsNil) 233 cfg, err = cfg.Apply(updates) 234 c.Assert(err, jc.ErrorIsNil) 235 return cfg 236 } 237 238 func (s *BaseSuiteUnpatched) UpdateConfig(c *gc.C, attrs map[string]interface{}) { 239 cfg, err := s.Config.Apply(attrs) 240 c.Assert(err, jc.ErrorIsNil) 241 s.setConfig(c, cfg) 242 } 243 244 func (s *BaseSuiteUnpatched) NewContainer(c *gc.C, name string) *containerlxd.Container { 245 metadata := make(map[string]string) 246 for k, v := range s.Metadata { 247 metadata[k] = v 248 } 249 250 return &containerlxd.Container{ 251 Instance: api.Instance{ 252 Name: name, 253 StatusCode: api.Running, 254 Status: api.Running.String(), 255 InstancePut: api.InstancePut{ 256 Config: metadata, 257 }, 258 Type: "container", 259 }, 260 } 261 } 262 263 func (s *BaseSuiteUnpatched) NewInstance(c *gc.C, name string) *environInstance { 264 container := s.NewContainer(c, name) 265 return newInstance(container, s.Env) 266 } 267 268 type BaseSuite struct { 269 BaseSuiteUnpatched 270 271 Stub *jujutesting.Stub 272 Client *StubClient 273 Common *stubCommon 274 } 275 276 func (s *BaseSuite) SetUpSuite(c *gc.C) { 277 s.BaseSuiteUnpatched.SetUpSuite(c) 278 // Do this *before* s.initEnv() gets called in BaseSuiteUnpatched.SetUpTest 279 } 280 281 func (s *BaseSuite) SetUpTest(c *gc.C) { 282 testing.SkipLXDNotSupported(c) 283 s.BaseSuiteUnpatched.SetUpTest(c) 284 285 s.Stub = &jujutesting.Stub{} 286 s.Client = &StubClient{ 287 Stub: s.Stub, 288 StorageIsSupported: true, 289 Server: &api.Server{ 290 ServerPut: api.ServerPut{ 291 Config: map[string]interface{}{}, 292 }, 293 Environment: api.ServerEnvironment{ 294 Certificate: "server-cert", 295 }, 296 }, 297 Profile: &api.Profile{}, 298 } 299 s.Common = &stubCommon{stub: s.Stub} 300 301 // Patch out all expensive external deps. 302 s.Env.serverUnlocked = s.Client 303 s.Env.base = s.Common 304 } 305 306 func (s *BaseSuite) TestingCert(c *gc.C) (lxd.Certificate, string) { 307 cert := lxd.Certificate{ 308 Name: "juju", 309 CertPEM: []byte(testing.CACert), 310 KeyPEM: []byte(testing.CAKey), 311 } 312 fingerprint, err := cert.Fingerprint() 313 c.Assert(err, jc.ErrorIsNil) 314 return cert, fingerprint 315 } 316 317 func (s *BaseSuite) CheckNoAPI(c *gc.C) { 318 s.Stub.CheckCalls(c, nil) 319 } 320 321 func NewBaseConfig(c *gc.C) *config.Config { 322 var err error 323 cfg := testing.ModelConfig(c) 324 325 cfg, err = cfg.Apply(ConfigAttrs) 326 c.Assert(err, jc.ErrorIsNil) 327 328 return cfg 329 } 330 331 type ConfigValues struct{} 332 333 type Config struct { 334 *environConfig 335 } 336 337 func NewConfig(cfg *config.Config) *Config { 338 ecfg := newConfig(cfg) 339 return &Config{ecfg} 340 } 341 342 func (ecfg *Config) Values(c *gc.C) (ConfigValues, map[string]interface{}) { 343 c.Assert(ecfg.attrs, jc.DeepEquals, ecfg.UnknownAttrs()) 344 345 var values ConfigValues 346 extras := make(map[string]interface{}) 347 for k, v := range ecfg.attrs { 348 switch k { 349 default: 350 extras[k] = v 351 } 352 } 353 return values, extras 354 } 355 356 func (ecfg *Config) Apply(c *gc.C, updates map[string]interface{}) *Config { 357 cfg, err := ecfg.Config.Apply(updates) 358 c.Assert(err, jc.ErrorIsNil) 359 return NewConfig(cfg) 360 } 361 362 func (ecfg *Config) Validate() error { 363 return ecfg.validate() 364 } 365 366 type stubCommon struct { 367 stub *jujutesting.Stub 368 369 BootstrapResult *environs.BootstrapResult 370 } 371 372 func (sc *stubCommon) BootstrapEnv(ctx environs.BootstrapContext, callCtx context.ProviderCallContext, params environs.BootstrapParams) (*environs.BootstrapResult, error) { 373 sc.stub.AddCall("Bootstrap", ctx, callCtx, params) 374 if err := sc.stub.NextErr(); err != nil { 375 return nil, errors.Trace(err) 376 } 377 378 return sc.BootstrapResult, nil 379 } 380 381 func (sc *stubCommon) DestroyEnv(callCtx context.ProviderCallContext) error { 382 sc.stub.AddCall("Destroy", callCtx) 383 if err := sc.stub.NextErr(); err != nil { 384 return errors.Trace(err) 385 } 386 387 return nil 388 } 389 390 type StubClient struct { 391 *jujutesting.Stub 392 393 Containers []lxd.Container 394 Container *lxd.Container 395 Server *api.Server 396 Profile *api.Profile 397 StorageIsSupported bool 398 Volumes map[string][]api.StorageVolume 399 ServerCert string 400 ServerHostArch string 401 ServerVer string 402 NetworkNames []string 403 NetworkState map[string]api.NetworkState 404 } 405 406 func (conn *StubClient) FilterContainers(prefix string, statuses ...string) ([]lxd.Container, error) { 407 conn.AddCall("FilterContainers", prefix, statuses) 408 if err := conn.NextErr(); err != nil { 409 return nil, errors.Trace(err) 410 } 411 412 return conn.Containers, nil 413 } 414 415 func (conn *StubClient) CreateContainerFromSpec(spec lxd.ContainerSpec) (*lxd.Container, error) { 416 conn.AddCall("CreateContainerFromSpec", spec) 417 if err := conn.NextErr(); err != nil { 418 return nil, errors.Trace(err) 419 } 420 421 return conn.Container, nil 422 } 423 424 func (conn *StubClient) FindImage( 425 ctx stdcontext.Context, base corebase.Base, arch string, virtType instance.VirtType, sources []lxd.ServerSpec, copyLocal bool, callback environs.StatusCallbackFunc, 426 ) (lxd.SourcedImage, error) { 427 conn.AddCall("FindImage", base.DisplayString(), arch) 428 if err := conn.NextErr(); err != nil { 429 return lxd.SourcedImage{}, errors.Trace(err) 430 } 431 432 return lxd.SourcedImage{}, nil 433 } 434 435 func (conn *StubClient) CreateCertificate(cert api.CertificatesPost) error { 436 conn.AddCall("CreateCertificate", cert) 437 return conn.NextErr() 438 } 439 440 func (conn *StubClient) CreateClientCertificate(cert *lxd.Certificate) error { 441 conn.AddCall("CreateClientCertificate", cert) 442 return conn.NextErr() 443 } 444 445 func (conn *StubClient) DeleteCertificate(fingerprint string) error { 446 conn.AddCall("RemoveCertByFingerprint", fingerprint) 447 return conn.NextErr() 448 } 449 450 func (conn *StubClient) GetCertificate(fingerprint string) (*api.Certificate, string, error) { 451 conn.AddCall("GetCertificate", fingerprint) 452 return &api.Certificate{}, "", conn.NextErr() 453 } 454 455 func (conn *StubClient) GetServer() (*api.Server, string, error) { 456 conn.AddCall("ServerStatus") 457 if err := conn.NextErr(); err != nil { 458 return nil, "", err 459 } 460 return &api.Server{ 461 Environment: api.ServerEnvironment{ 462 Certificate: "server-cert", 463 }, 464 }, "etag", nil 465 } 466 467 func (conn *StubClient) ServerVersion() string { 468 conn.AddCall("ServerVersion") 469 return conn.ServerVer 470 } 471 472 func (conn *StubClient) GetConnectionInfo() (info *lxdclient.ConnectionInfo, err error) { 473 conn.AddCall("ServerAddresses") 474 return &lxdclient.ConnectionInfo{ 475 Addresses: []string{"127.0.0.1:1234", "1.2.3.4:1234"}, 476 }, conn.NextErr() 477 } 478 479 func (conn *StubClient) UpdateServerConfig(cfg map[string]string) error { 480 conn.AddCall("UpdateServerConfig", cfg) 481 return conn.NextErr() 482 } 483 484 func (conn *StubClient) UpdateContainerConfig(container string, cfg map[string]string) error { 485 conn.AddCall("UpdateContainerConfig", container, cfg) 486 return conn.NextErr() 487 } 488 489 func (conn *StubClient) LocalBridgeName() string { 490 conn.AddCall("LocalBridgeName") 491 return "test-bridge" 492 } 493 494 func (conn *StubClient) GetProfile(name string) (*api.Profile, string, error) { 495 conn.AddCall("GetProfile", name) 496 return conn.Profile, "etag", conn.NextErr() 497 } 498 499 func (conn *StubClient) GetContainerProfiles(name string) ([]string, error) { 500 conn.AddCall("GetContainerProfiles", name) 501 return []string{ 502 "default", 503 "juju-model-name", 504 }, conn.NextErr() 505 } 506 507 func (conn *StubClient) DeleteProfile(name string) error { 508 conn.AddCall("DeleteProfile", name) 509 return conn.NextErr() 510 } 511 512 func (conn *StubClient) HasProfile(name string) (bool, error) { 513 conn.AddCall("HasProfile", name) 514 return false, conn.NextErr() 515 } 516 517 func (conn *StubClient) ReplaceOrAddContainerProfile(name, oldProfile, newProfile string) error { 518 conn.AddCall("ReplaceOrAddContainerProfile", name, oldProfile, newProfile) 519 return conn.NextErr() 520 } 521 522 func (conn *StubClient) UpdateContainerProfiles(name string, profiles []string) error { 523 conn.AddCall("UpdateContainerProfiles", name, profiles) 524 return conn.NextErr() 525 } 526 527 func (conn *StubClient) VerifyNetworkDevice(profile *api.Profile, ETag string) error { 528 conn.AddCall("VerifyNetworkDevice", profile, ETag) 529 return conn.NextErr() 530 } 531 532 func (conn *StubClient) StorageSupported() bool { 533 conn.AddCall("StorageSupported") 534 return conn.StorageIsSupported 535 } 536 537 func (conn *StubClient) EnsureDefaultStorage(profile *api.Profile, ETag string) error { 538 conn.AddCall("EnsureDefaultStorage", profile, ETag) 539 return conn.NextErr() 540 } 541 542 func (conn *StubClient) GetStoragePool(name string) (pool *api.StoragePool, ETag string, err error) { 543 conn.AddCall("GetStoragePool", name) 544 return &api.StoragePool{ 545 Name: name, 546 Driver: "dir", 547 }, "", conn.NextErr() 548 } 549 550 func (conn *StubClient) GetStoragePools() ([]api.StoragePool, error) { 551 conn.AddCall("GetStoragePools") 552 return []api.StoragePool{{ 553 Name: "juju", 554 Driver: "dir", 555 }, { 556 Name: "juju-zfs", 557 Driver: "zfs", 558 }}, conn.NextErr() 559 } 560 561 func (conn *StubClient) CreatePool(name, driver string, attrs map[string]string) error { 562 conn.AddCall("CreatePool", name, driver, attrs) 563 return conn.NextErr() 564 } 565 566 func (conn *StubClient) CreateVolume(pool, volume string, config map[string]string) error { 567 conn.AddCall("CreateVolume", pool, volume, config) 568 return conn.NextErr() 569 } 570 571 func (conn *StubClient) DeleteStoragePoolVolume(pool, volType, volume string) error { 572 conn.AddCall("DeleteStoragePoolVolume", pool, volType, volume) 573 return conn.NextErr() 574 } 575 576 func (conn *StubClient) GetStoragePoolVolume( 577 pool string, volType string, name string, 578 ) (*api.StorageVolume, string, error) { 579 conn.AddCall("GetStoragePoolVolume", pool, volType, name) 580 if err := conn.NextErr(); err != nil { 581 return nil, "", err 582 } 583 for _, v := range conn.Volumes[pool] { 584 if v.Name == name { 585 return &v, "eTag", nil 586 } 587 } 588 return nil, "", errors.NotFoundf("volume %q in pool %q", name, pool) 589 } 590 591 func (conn *StubClient) GetStoragePoolVolumes(pool string) ([]api.StorageVolume, error) { 592 conn.AddCall("GetStoragePoolVolumes", pool) 593 if err := conn.NextErr(); err != nil { 594 return nil, err 595 } 596 return conn.Volumes[pool], nil 597 } 598 599 func (conn *StubClient) UpdateStoragePoolVolume( 600 pool string, volType string, name string, volume api.StorageVolumePut, ETag string, 601 ) error { 602 conn.AddCall("UpdateStoragePoolVolume", pool, volType, name, volume, ETag) 603 return conn.NextErr() 604 } 605 606 func (conn *StubClient) AliveContainers(prefix string) ([]lxd.Container, error) { 607 conn.AddCall("AliveContainers", prefix) 608 if err := conn.NextErr(); err != nil { 609 return nil, err 610 } 611 return conn.Containers, nil 612 } 613 614 func (conn *StubClient) ContainerAddresses(name string) ([]network.ProviderAddress, error) { 615 conn.AddCall("ContainerAddresses", name) 616 if err := conn.NextErr(); err != nil { 617 return nil, err 618 } 619 620 return network.ProviderAddresses{ 621 network.NewMachineAddress("10.0.0.1", network.WithScope(network.ScopeCloudLocal)).AsProviderAddress(), 622 }, nil 623 } 624 625 func (conn *StubClient) RemoveContainer(name string) error { 626 conn.AddCall("RemoveContainer", name) 627 return conn.NextErr() 628 } 629 630 func (conn *StubClient) RemoveContainers(names []string) error { 631 conn.AddCall("RemoveContainers", names) 632 return conn.NextErr() 633 } 634 635 func (conn *StubClient) WriteContainer(container *lxd.Container) error { 636 conn.AddCall("WriteContainer", container) 637 return conn.NextErr() 638 } 639 640 func (conn *StubClient) CreateProfileWithConfig(name string, cfg map[string]string) error { 641 conn.AddCall("CreateProfileWithConfig", name, cfg) 642 return conn.NextErr() 643 } 644 645 func (conn *StubClient) CreateProfile(post api.ProfilesPost) error { 646 conn.AddCall("CreateProfile", post) 647 return conn.NextErr() 648 } 649 650 func (conn *StubClient) ServerCertificate() string { 651 conn.AddCall("ServerCertificate") 652 return conn.ServerCert 653 } 654 655 func (conn *StubClient) HostArch() string { 656 conn.AddCall("HostArch") 657 return conn.ServerHostArch 658 } 659 660 func (conn *StubClient) SupportedArches() []string { 661 conn.AddCall("SupportedArches") 662 return []string{conn.ServerHostArch} 663 } 664 665 func (conn *StubClient) EnableHTTPSListener() error { 666 conn.AddCall("EnableHTTPSListener") 667 return conn.NextErr() 668 } 669 670 func (conn *StubClient) GetNICsFromProfile(profName string) (map[string]map[string]string, error) { 671 conn.AddCall("GetNICsFromProfile", profName) 672 return conn.Profile.Devices, conn.NextErr() 673 } 674 675 func (conn *StubClient) IsClustered() bool { 676 conn.AddCall("IsClustered") 677 return true 678 } 679 680 func (conn *StubClient) Name() string { 681 conn.AddCall("Name") 682 return "server" 683 } 684 685 func (conn *StubClient) UseProject(string) { 686 panic("this stub is deprecated; use mocks instead") 687 } 688 689 func (*StubClient) HasExtension(_ string) bool { 690 panic("this stub is deprecated; use mocks instead") 691 } 692 693 func (conn *StubClient) GetNetworks() ([]api.Network, error) { 694 panic("this stub is deprecated; use mocks instead") 695 } 696 697 func (*StubClient) GetNetworkState(string) (*api.NetworkState, error) { 698 panic("this stub is deprecated; use mocks instead") 699 } 700 701 func (*StubClient) GetInstance(string) (*api.Instance, string, error) { 702 panic("this stub is deprecated; use mocks instead") 703 } 704 705 func (*StubClient) GetInstanceState(string) (*api.InstanceState, string, error) { 706 panic("this stub is deprecated; use mocks instead") 707 } 708 709 // TODO (manadart 2018-07-20): This exists to satisfy the testing stub 710 // interface. It is temporary, pending replacement with mocks and 711 // should not be called in tests. 712 func (conn *StubClient) UseTargetServer(name string) (*lxd.Server, error) { 713 conn.AddCall("UseTargetServer", name) 714 return nil, conn.NextErr() 715 } 716 717 func (conn *StubClient) GetClusterMembers() (members []api.ClusterMember, err error) { 718 conn.AddCall("GetClusterMembers") 719 return nil, conn.NextErr() 720 } 721 722 type MockClock struct { 723 clock.Clock 724 now time.Time 725 } 726 727 func (m *MockClock) Now() time.Time { 728 return m.now 729 } 730 731 func (m *MockClock) After(delay time.Duration) <-chan time.Time { 732 return time.After(time.Millisecond) 733 } 734 735 // TODO (manadart 2018-07-20): All of the above logic should ultimately be 736 // replaced by what follows (in some form). The stub usage will be abandoned 737 // and replaced by mocks. 738 739 type EnvironSuite struct { 740 testing.BaseSuite 741 } 742 743 func (s *EnvironSuite) NewEnviron(c *gc.C, srv Server, cfgEdit map[string]interface{}, cloudSpec environscloudspec.CloudSpec) environs.Environ { 744 cfg, err := testing.ModelConfig(c).Apply(ConfigAttrs) 745 c.Assert(err, jc.ErrorIsNil) 746 747 if cfgEdit != nil { 748 var err error 749 cfg, err = cfg.Apply(cfgEdit) 750 c.Assert(err, jc.ErrorIsNil) 751 } 752 753 eCfg, err := newValidConfig(cfg) 754 c.Assert(err, jc.ErrorIsNil) 755 756 namespace, err := instance.NewNamespace(cfg.UUID()) 757 c.Assert(err, jc.ErrorIsNil) 758 759 return &environ{ 760 serverUnlocked: srv, 761 ecfgUnlocked: eCfg, 762 namespace: namespace, 763 cloud: cloudSpec, 764 } 765 } 766 767 func (s *EnvironSuite) NewEnvironWithServerFactory(c *gc.C, srv ServerFactory, cfgEdit map[string]interface{}) environs.Environ { 768 cfg, err := testing.ModelConfig(c).Apply(ConfigAttrs) 769 c.Assert(err, jc.ErrorIsNil) 770 771 if cfgEdit != nil { 772 var err error 773 cfg, err = cfg.Apply(cfgEdit) 774 c.Assert(err, jc.ErrorIsNil) 775 } 776 777 eCfg, err := newValidConfig(cfg) 778 c.Assert(err, jc.ErrorIsNil) 779 780 namespace, err := instance.NewNamespace(cfg.UUID()) 781 c.Assert(err, jc.ErrorIsNil) 782 783 provid := environProvider{ 784 serverFactory: srv, 785 } 786 787 return &environ{ 788 name: "controller", 789 provider: &provid, 790 ecfgUnlocked: eCfg, 791 namespace: namespace, 792 } 793 } 794 795 func (s *EnvironSuite) GetStartInstanceArgs(c *gc.C) environs.StartInstanceParams { 796 tools := []*coretools.Tools{ 797 { 798 Version: version.Binary{Arch: arch.AMD64, Release: "ubuntu"}, 799 URL: "https://example.org/amd", 800 }, 801 { 802 Version: version.Binary{Arch: arch.ARM64, Release: "ubuntu"}, 803 URL: "https://example.org/arm", 804 }, 805 } 806 807 cons := constraints.Value{} 808 iConfig, err := instancecfg.NewBootstrapInstanceConfig(testing.FakeControllerConfig(), cons, cons, 809 jujuversion.DefaultSupportedLTSBase(), "", nil) 810 c.Assert(err, jc.ErrorIsNil) 811 812 return environs.StartInstanceParams{ 813 ControllerUUID: iConfig.ControllerConfig.ControllerUUID(), 814 InstanceConfig: iConfig, 815 Tools: tools, 816 Constraints: cons, 817 } 818 }