github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/provider/lxd/testing_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 // +build go1.3 5 6 package lxd 7 8 import ( 9 "os" 10 11 "github.com/juju/errors" 12 gitjujutesting "github.com/juju/testing" 13 jc "github.com/juju/testing/checkers" 14 "github.com/juju/utils/arch" 15 "github.com/lxc/lxd/shared" 16 gc "gopkg.in/check.v1" 17 18 "github.com/juju/juju/cloudconfig/instancecfg" 19 "github.com/juju/juju/cloudconfig/providerinit" 20 "github.com/juju/juju/constraints" 21 "github.com/juju/juju/environs" 22 "github.com/juju/juju/environs/config" 23 "github.com/juju/juju/environs/tags" 24 "github.com/juju/juju/instance" 25 "github.com/juju/juju/network" 26 "github.com/juju/juju/testing" 27 coretools "github.com/juju/juju/tools" 28 "github.com/juju/juju/tools/lxdclient" 29 "github.com/juju/version" 30 ) 31 32 // Ensure LXD provider supports the expected interfaces. 33 var ( 34 _ config.ConfigSchemaSource = (*environProvider)(nil) 35 ) 36 37 // These values are stub LXD client credentials for use in tests. 38 const ( 39 PublicKey = `-----BEGIN CERTIFICATE----- 40 ... 41 ... 42 ... 43 ... 44 ... 45 ... 46 ... 47 ... 48 ... 49 ... 50 ... 51 ... 52 ... 53 ... 54 -----END CERTIFICATE----- 55 ` 56 PrivateKey = `-----BEGIN PRIVATE KEY----- 57 ... 58 ... 59 ... 60 ... 61 ... 62 ... 63 ... 64 ... 65 ... 66 ... 67 ... 68 ... 69 ... 70 ... 71 -----END PRIVATE KEY----- 72 ` 73 ) 74 75 // These are stub config values for use in tests. 76 var ( 77 ConfigAttrs = testing.FakeConfig().Merge(testing.Attrs{ 78 "type": "lxd", 79 "uuid": "2d02eeac-9dbb-11e4-89d3-123b93f75cba", 80 }) 81 ) 82 83 // We test these here since they are not exported. 84 var ( 85 _ environs.Environ = (*environ)(nil) 86 _ instance.Instance = (*environInstance)(nil) 87 ) 88 89 type BaseSuiteUnpatched struct { 90 gitjujutesting.IsolationSuite 91 92 osPathOrig string 93 94 Config *config.Config 95 EnvConfig *environConfig 96 Env *environ 97 98 Addresses []network.Address 99 Instance *environInstance 100 RawInstance *lxdclient.Instance 101 InstName string 102 Hardware *lxdclient.InstanceHardware 103 HWC *instance.HardwareCharacteristics 104 Metadata map[string]string 105 StartInstArgs environs.StartInstanceParams 106 //InstanceType instances.InstanceType 107 108 Ports []network.PortRange 109 } 110 111 func (s *BaseSuiteUnpatched) SetUpSuite(c *gc.C) { 112 s.osPathOrig = os.Getenv("PATH") 113 if s.osPathOrig == "" { 114 // TODO(ericsnow) This shouldn't happen. However, an undiagnosed 115 // bug in testing.IsolationSuite is causing $PATH to remain unset 116 // sometimes. Once that is cleared up this special-case can go 117 // away. 118 s.osPathOrig = 119 "/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin" 120 } 121 s.IsolationSuite.SetUpSuite(c) 122 } 123 124 func (s *BaseSuiteUnpatched) SetUpTest(c *gc.C) { 125 s.IsolationSuite.SetUpTest(c) 126 127 s.initEnv(c) 128 s.initInst(c) 129 s.initNet(c) 130 } 131 132 func (s *BaseSuiteUnpatched) initEnv(c *gc.C) { 133 s.Env = &environ{ 134 name: "lxd", 135 } 136 cfg := s.NewConfig(c, nil) 137 s.setConfig(c, cfg) 138 } 139 140 func (s *BaseSuiteUnpatched) Prefix() string { 141 return s.Env.namespace.Prefix() 142 } 143 144 func (s *BaseSuiteUnpatched) initInst(c *gc.C) { 145 tools := []*coretools.Tools{ 146 { 147 Version: version.Binary{Arch: arch.AMD64, Series: "trusty"}, 148 URL: "https://example.org/amd", 149 }, 150 { 151 Version: version.Binary{Arch: arch.ARM64, Series: "trusty"}, 152 URL: "https://example.org/arm", 153 }, 154 } 155 156 cons := constraints.Value{ 157 // nothing 158 } 159 160 instanceConfig, err := instancecfg.NewBootstrapInstanceConfig(testing.FakeControllerConfig(), cons, cons, "trusty", "") 161 c.Assert(err, jc.ErrorIsNil) 162 163 err = instanceConfig.SetTools(coretools.List{ 164 tools[0], 165 }) 166 c.Assert(err, jc.ErrorIsNil) 167 instanceConfig.AuthorizedKeys = s.Config.AuthorizedKeys() 168 169 userData, err := providerinit.ComposeUserData(instanceConfig, nil, lxdRenderer{}) 170 c.Assert(err, jc.ErrorIsNil) 171 172 s.Hardware = &lxdclient.InstanceHardware{ 173 Architecture: arch.ARM64, 174 NumCores: 1, 175 MemoryMB: 3750, 176 } 177 var archName string = arch.ARM64 178 var numCores uint64 = 1 179 var memoryMB uint64 = 3750 180 s.HWC = &instance.HardwareCharacteristics{ 181 Arch: &archName, 182 CpuCores: &numCores, 183 Mem: &memoryMB, 184 } 185 186 s.Metadata = map[string]string{ // userdata 187 tags.JujuIsController: "true", 188 tags.JujuController: testing.ControllerTag.Id(), 189 tags.JujuModel: s.Config.UUID(), 190 metadataKeyCloudInit: string(userData), 191 } 192 s.Addresses = []network.Address{{ 193 Value: "10.0.0.1", 194 Type: network.IPv4Address, 195 Scope: network.ScopeCloudLocal, 196 }} 197 // NOTE: the instance ids used throughout this package are not at all 198 // representative of what they would normally be. They would normally 199 // determined by the instance namespace and the machine id. 200 s.Instance = s.NewInstance(c, "spam") 201 s.RawInstance = s.Instance.raw 202 s.InstName, err = s.Env.namespace.Hostname("42") 203 c.Assert(err, jc.ErrorIsNil) 204 205 s.StartInstArgs = environs.StartInstanceParams{ 206 ControllerUUID: instanceConfig.Controller.Config.ControllerUUID(), 207 InstanceConfig: instanceConfig, 208 Tools: tools, 209 Constraints: cons, 210 } 211 } 212 213 func (s *BaseSuiteUnpatched) initNet(c *gc.C) { 214 s.Ports = []network.PortRange{{ 215 FromPort: 80, 216 ToPort: 80, 217 Protocol: "tcp", 218 }} 219 } 220 221 func (s *BaseSuiteUnpatched) setConfig(c *gc.C, cfg *config.Config) { 222 s.Config = cfg 223 ecfg, err := newValidConfig(cfg) 224 c.Assert(err, jc.ErrorIsNil) 225 s.EnvConfig = ecfg 226 uuid := cfg.UUID() 227 s.Env.uuid = uuid 228 s.Env.ecfg = s.EnvConfig 229 namespace, err := instance.NewNamespace(uuid) 230 c.Assert(err, jc.ErrorIsNil) 231 s.Env.namespace = namespace 232 } 233 234 func (s *BaseSuiteUnpatched) NewConfig(c *gc.C, updates testing.Attrs) *config.Config { 235 if updates == nil { 236 updates = make(testing.Attrs) 237 } 238 var err error 239 cfg := testing.ModelConfig(c) 240 cfg, err = cfg.Apply(ConfigAttrs) 241 c.Assert(err, jc.ErrorIsNil) 242 cfg, err = cfg.Apply(updates) 243 c.Assert(err, jc.ErrorIsNil) 244 return cfg 245 } 246 247 func (s *BaseSuiteUnpatched) UpdateConfig(c *gc.C, attrs map[string]interface{}) { 248 cfg, err := s.Config.Apply(attrs) 249 c.Assert(err, jc.ErrorIsNil) 250 s.setConfig(c, cfg) 251 } 252 253 func (s *BaseSuiteUnpatched) NewRawInstance(c *gc.C, name string) *lxdclient.Instance { 254 metadata := make(map[string]string) 255 for k, v := range s.Metadata { 256 metadata[k] = v 257 } 258 summary := lxdclient.InstanceSummary{ 259 Name: name, 260 Status: lxdclient.StatusRunning, 261 Hardware: *s.Hardware, 262 Metadata: metadata, 263 } 264 instanceSpec := lxdclient.InstanceSpec{ 265 Name: name, 266 Profiles: []string{}, 267 Ephemeral: false, 268 Metadata: metadata, 269 } 270 return lxdclient.NewInstance(summary, &instanceSpec) 271 } 272 273 func (s *BaseSuiteUnpatched) NewInstance(c *gc.C, name string) *environInstance { 274 raw := s.NewRawInstance(c, name) 275 return newInstance(raw, s.Env) 276 } 277 278 func (s *BaseSuiteUnpatched) IsRunningLocally(c *gc.C) bool { 279 restore := gitjujutesting.PatchEnvPathPrepend(s.osPathOrig) 280 defer restore() 281 282 running, err := lxdclient.IsRunningLocally() 283 c.Assert(err, jc.ErrorIsNil) 284 return running 285 } 286 287 type BaseSuite struct { 288 BaseSuiteUnpatched 289 290 Stub *gitjujutesting.Stub 291 Client *StubClient 292 Firewaller *stubFirewaller 293 Common *stubCommon 294 } 295 296 func (s *BaseSuite) SetUpSuite(c *gc.C) { 297 s.BaseSuiteUnpatched.SetUpSuite(c) 298 // Do this *before* s.initEnv() gets called in BaseSuiteUnpatched.SetUpTest 299 } 300 301 func (s *BaseSuite) SetUpTest(c *gc.C) { 302 s.BaseSuiteUnpatched.SetUpTest(c) 303 304 s.Stub = &gitjujutesting.Stub{} 305 s.Client = &StubClient{Stub: s.Stub} 306 s.Firewaller = &stubFirewaller{stub: s.Stub} 307 s.Common = &stubCommon{stub: s.Stub} 308 309 // Patch out all expensive external deps. 310 s.Env.raw = &rawProvider{ 311 lxdCerts: s.Client, 312 lxdConfig: s.Client, 313 lxdInstances: s.Client, 314 lxdImages: s.Client, 315 Firewaller: s.Firewaller, 316 } 317 s.Env.base = s.Common 318 } 319 320 func (s *BaseSuite) CheckNoAPI(c *gc.C) { 321 s.Stub.CheckCalls(c, nil) 322 } 323 324 func NewBaseConfig(c *gc.C) *config.Config { 325 var err error 326 cfg := testing.ModelConfig(c) 327 328 cfg, err = cfg.Apply(ConfigAttrs) 329 c.Assert(err, jc.ErrorIsNil) 330 331 return cfg 332 } 333 334 func NewCustomBaseConfig(c *gc.C, updates map[string]interface{}) *config.Config { 335 if updates == nil { 336 updates = make(testing.Attrs) 337 } 338 339 cfg := NewBaseConfig(c) 340 341 cfg, err := cfg.Apply(updates) 342 c.Assert(err, jc.ErrorIsNil) 343 344 return cfg 345 } 346 347 type ConfigValues struct { 348 } 349 350 type Config struct { 351 *environConfig 352 } 353 354 func NewConfig(cfg *config.Config) *Config { 355 ecfg := newConfig(cfg) 356 return &Config{ecfg} 357 } 358 359 func (ecfg *Config) Values(c *gc.C) (ConfigValues, map[string]interface{}) { 360 c.Assert(ecfg.attrs, jc.DeepEquals, ecfg.UnknownAttrs()) 361 362 var values ConfigValues 363 extras := make(map[string]interface{}) 364 for k, v := range ecfg.attrs { 365 switch k { 366 default: 367 extras[k] = v 368 } 369 } 370 return values, extras 371 } 372 373 func (ecfg *Config) Apply(c *gc.C, updates map[string]interface{}) *Config { 374 cfg, err := ecfg.Config.Apply(updates) 375 c.Assert(err, jc.ErrorIsNil) 376 return NewConfig(cfg) 377 } 378 379 func (ecfg *Config) Validate() error { 380 return ecfg.validate() 381 } 382 383 type stubCommon struct { 384 stub *gitjujutesting.Stub 385 386 BootstrapResult *environs.BootstrapResult 387 } 388 389 func (sc *stubCommon) BootstrapEnv(ctx environs.BootstrapContext, params environs.BootstrapParams) (*environs.BootstrapResult, error) { 390 sc.stub.AddCall("Bootstrap", ctx, params) 391 if err := sc.stub.NextErr(); err != nil { 392 return nil, errors.Trace(err) 393 } 394 395 return sc.BootstrapResult, nil 396 } 397 398 func (sc *stubCommon) DestroyEnv() error { 399 sc.stub.AddCall("Destroy") 400 if err := sc.stub.NextErr(); err != nil { 401 return errors.Trace(err) 402 } 403 404 return nil 405 } 406 407 type StubClient struct { 408 *gitjujutesting.Stub 409 410 Insts []lxdclient.Instance 411 Inst *lxdclient.Instance 412 } 413 414 func (conn *StubClient) Instances(prefix string, statuses ...string) ([]lxdclient.Instance, error) { 415 conn.AddCall("Instances", prefix, statuses) 416 if err := conn.NextErr(); err != nil { 417 return nil, errors.Trace(err) 418 } 419 420 return conn.Insts, nil 421 } 422 423 func (conn *StubClient) AddInstance(spec lxdclient.InstanceSpec) (*lxdclient.Instance, error) { 424 conn.AddCall("AddInstance", spec) 425 if err := conn.NextErr(); err != nil { 426 return nil, errors.Trace(err) 427 } 428 429 return conn.Inst, nil 430 } 431 432 func (conn *StubClient) RemoveInstances(prefix string, ids ...string) error { 433 conn.AddCall("RemoveInstances", prefix, ids) 434 if err := conn.NextErr(); err != nil { 435 return errors.Trace(err) 436 } 437 438 return nil 439 } 440 441 func (conn *StubClient) EnsureImageExists(series string, _ []lxdclient.Remote, _ func(string)) error { 442 conn.AddCall("EnsureImageExists", series) 443 if err := conn.NextErr(); err != nil { 444 return errors.Trace(err) 445 } 446 447 return nil 448 } 449 450 func (conn *StubClient) Addresses(name string) ([]network.Address, error) { 451 conn.AddCall("Addresses", name) 452 if err := conn.NextErr(); err != nil { 453 return nil, errors.Trace(err) 454 } 455 456 return []network.Address{network.Address{ 457 Value: "10.0.0.1", 458 Type: network.IPv4Address, 459 Scope: network.ScopeCloudLocal, 460 }}, nil 461 } 462 463 func (conn *StubClient) AddCert(cert lxdclient.Cert) error { 464 conn.AddCall("AddCert", cert) 465 return conn.NextErr() 466 } 467 468 func (conn *StubClient) RemoveCertByFingerprint(fingerprint string) error { 469 conn.AddCall("RemoveCertByFingerprint", fingerprint) 470 return conn.NextErr() 471 } 472 473 func (conn *StubClient) ServerStatus() (*shared.ServerState, error) { 474 conn.AddCall("ServerStatus") 475 if err := conn.NextErr(); err != nil { 476 return nil, err 477 } 478 return &shared.ServerState{ 479 Environment: shared.ServerStateEnvironment{ 480 Certificate: "server-cert", 481 }, 482 }, nil 483 } 484 485 func (conn *StubClient) SetConfig(k, v string) error { 486 conn.AddCall("SetConfig", k, v) 487 return conn.NextErr() 488 } 489 490 // TODO(ericsnow) Move stubFirewaller to environs/testing or provider/common/testing. 491 492 type stubFirewaller struct { 493 stub *gitjujutesting.Stub 494 495 PortRanges []network.PortRange 496 } 497 498 func (fw *stubFirewaller) Ports(fwname string) ([]network.PortRange, error) { 499 fw.stub.AddCall("Ports", fwname) 500 if err := fw.stub.NextErr(); err != nil { 501 return nil, errors.Trace(err) 502 } 503 504 return fw.PortRanges, nil 505 } 506 507 func (fw *stubFirewaller) OpenPorts(fwname string, ports ...network.PortRange) error { 508 fw.stub.AddCall("OpenPorts", fwname, ports) 509 if err := fw.stub.NextErr(); err != nil { 510 return errors.Trace(err) 511 } 512 513 return nil 514 } 515 516 func (fw *stubFirewaller) ClosePorts(fwname string, ports ...network.PortRange) error { 517 fw.stub.AddCall("ClosePorts", fwname, ports) 518 if err := fw.stub.NextErr(); err != nil { 519 return errors.Trace(err) 520 } 521 522 return nil 523 }