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