github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/provider/gce/testing_test.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package gce 5 6 import ( 7 "encoding/base64" 8 "fmt" 9 "strings" 10 11 gitjujutesting "github.com/juju/testing" 12 jc "github.com/juju/testing/checkers" 13 gc "gopkg.in/check.v1" 14 15 "github.com/juju/juju/cloudconfig/instancecfg" 16 "github.com/juju/juju/cloudconfig/providerinit" 17 "github.com/juju/juju/constraints" 18 "github.com/juju/juju/environs" 19 "github.com/juju/juju/environs/config" 20 "github.com/juju/juju/environs/imagemetadata" 21 "github.com/juju/juju/environs/instances" 22 "github.com/juju/juju/environs/simplestreams" 23 "github.com/juju/juju/instance" 24 "github.com/juju/juju/juju/arch" 25 "github.com/juju/juju/network" 26 "github.com/juju/juju/provider/common" 27 "github.com/juju/juju/provider/gce/google" 28 "github.com/juju/juju/testing" 29 "github.com/juju/juju/tools" 30 "github.com/juju/juju/version" 31 ) 32 33 // These values are fake GCE auth credentials for use in tests. 34 const ( 35 ClientName = "ba9876543210-0123456789abcdefghijklmnopqrstuv" 36 ClientID = ClientName + ".apps.googleusercontent.com" 37 ClientEmail = ClientName + "@developer.gserviceaccount.com" 38 PrivateKey = `-----BEGIN PRIVATE KEY----- 39 ... 40 ... 41 ... 42 ... 43 ... 44 ... 45 ... 46 ... 47 ... 48 ... 49 ... 50 ... 51 ... 52 ... 53 -----END PRIVATE KEY----- 54 ` 55 ) 56 57 // These are fake config values for use in tests. 58 var ( 59 AuthFile = fmt.Sprintf(`{ 60 "private_key_id": "abcdef0123456789abcdef0123456789abcdef01", 61 "private_key": "%s", 62 "client_email": "%s", 63 "client_id": "%s", 64 "type": "service_account" 65 }`, strings.Replace(PrivateKey, "\n", "\\n", -1), ClientEmail, ClientID) 66 67 ConfigAttrs = testing.FakeConfig().Merge(testing.Attrs{ 68 "type": "gce", 69 "auth-file": "", 70 "private-key": PrivateKey, 71 "client-id": ClientID, 72 "client-email": ClientEmail, 73 "region": "home", 74 "project-id": "my-juju", 75 "image-endpoint": "https://www.googleapis.com", 76 "uuid": "2d02eeac-9dbb-11e4-89d3-123b93f75cba", 77 }) 78 ) 79 80 type BaseSuiteUnpatched struct { 81 gitjujutesting.IsolationSuite 82 83 Config *config.Config 84 EnvConfig *environConfig 85 Env *environ 86 Prefix string 87 88 Addresses []network.Address 89 BaseInstance *google.Instance 90 Instance *environInstance 91 InstName string 92 Metadata map[string]string 93 StartInstArgs environs.StartInstanceParams 94 InstanceType instances.InstanceType 95 96 Ports []network.PortRange 97 } 98 99 var _ environs.Environ = (*environ)(nil) 100 var _ simplestreams.HasRegion = (*environ)(nil) 101 var _ instance.Instance = (*environInstance)(nil) 102 103 func (s *BaseSuiteUnpatched) SetUpTest(c *gc.C) { 104 s.IsolationSuite.SetUpTest(c) 105 106 s.initEnv(c) 107 s.initInst(c) 108 s.initNet(c) 109 } 110 111 func (s *BaseSuiteUnpatched) initEnv(c *gc.C) { 112 s.Env = &environ{ 113 name: "google", 114 } 115 cfg := s.NewConfig(c, nil) 116 s.setConfig(c, cfg) 117 } 118 119 func (s *BaseSuiteUnpatched) initInst(c *gc.C) { 120 tools := []*tools.Tools{{ 121 Version: version.Binary{Arch: arch.AMD64, Series: "trusty"}, 122 URL: "https://example.org", 123 }} 124 125 cons := constraints.Value{InstanceType: &allInstanceTypes[0].Name} 126 127 instanceConfig, err := instancecfg.NewBootstrapInstanceConfig(cons, "trusty") 128 c.Assert(err, jc.ErrorIsNil) 129 130 instanceConfig.Tools = tools[0] 131 instanceConfig.AuthorizedKeys = s.Config.AuthorizedKeys() 132 133 userData, err := providerinit.ComposeUserData(instanceConfig, nil) 134 c.Assert(err, jc.ErrorIsNil) 135 b64UserData := base64.StdEncoding.EncodeToString([]byte(userData)) 136 137 authKeys, err := google.FormatAuthorizedKeys(instanceConfig.AuthorizedKeys, "ubuntu") 138 c.Assert(err, jc.ErrorIsNil) 139 140 s.Metadata = map[string]string{ 141 metadataKeyIsState: metadataValueTrue, 142 metadataKeyCloudInit: b64UserData, 143 metadataKeyEncoding: "base64", 144 metadataKeySSHKeys: authKeys, 145 } 146 s.Addresses = []network.Address{{ 147 Value: "10.0.0.1", 148 Type: network.IPv4Address, 149 Scope: network.ScopeCloudLocal, 150 }} 151 s.Instance = s.NewInstance(c, "spam") 152 s.BaseInstance = s.Instance.base 153 s.InstName = s.Prefix + "machine-spam" 154 155 s.StartInstArgs = environs.StartInstanceParams{ 156 InstanceConfig: instanceConfig, 157 Tools: tools, 158 Constraints: cons, 159 //Placement: "", 160 //DistributionGroup: nil, 161 } 162 163 s.InstanceType = allInstanceTypes[0] 164 } 165 166 func (s *BaseSuiteUnpatched) initNet(c *gc.C) { 167 s.Ports = []network.PortRange{{ 168 FromPort: 80, 169 ToPort: 80, 170 Protocol: "tcp", 171 }} 172 } 173 174 func (s *BaseSuiteUnpatched) setConfig(c *gc.C, cfg *config.Config) { 175 s.Config = cfg 176 ecfg, err := newValidConfig(cfg, configDefaults) 177 c.Assert(err, jc.ErrorIsNil) 178 s.EnvConfig = ecfg 179 uuid, _ := cfg.UUID() 180 s.Env.uuid = uuid 181 s.Env.ecfg = s.EnvConfig 182 s.Prefix = "juju-" + uuid + "-" 183 } 184 185 func (s *BaseSuiteUnpatched) NewConfig(c *gc.C, updates testing.Attrs) *config.Config { 186 var err error 187 cfg := testing.EnvironConfig(c) 188 cfg, err = cfg.Apply(ConfigAttrs) 189 c.Assert(err, jc.ErrorIsNil) 190 cfg, err = cfg.Apply(updates) 191 c.Assert(err, jc.ErrorIsNil) 192 return cfg 193 } 194 195 func (s *BaseSuiteUnpatched) UpdateConfig(c *gc.C, attrs map[string]interface{}) { 196 cfg, err := s.Config.Apply(attrs) 197 c.Assert(err, jc.ErrorIsNil) 198 s.setConfig(c, cfg) 199 } 200 201 func (s *BaseSuiteUnpatched) NewBaseInstance(c *gc.C, id string) *google.Instance { 202 diskSpec := google.DiskSpec{ 203 SizeHintGB: 15, 204 ImageURL: "some/image/path", 205 Boot: true, 206 Scratch: false, 207 Readonly: false, 208 AutoDelete: true, 209 } 210 instanceSpec := google.InstanceSpec{ 211 ID: id, 212 Type: "mtype", 213 Disks: []google.DiskSpec{diskSpec}, 214 Network: google.NetworkSpec{Name: "somenetwork"}, 215 NetworkInterfaces: []string{"somenetif"}, 216 Metadata: s.Metadata, 217 Tags: []string{id}, 218 } 219 summary := google.InstanceSummary{ 220 ID: id, 221 ZoneName: "home-zone", 222 Status: google.StatusRunning, 223 Metadata: s.Metadata, 224 Addresses: s.Addresses, 225 } 226 return google.NewInstance(summary, &instanceSpec) 227 } 228 229 func (s *BaseSuiteUnpatched) NewInstance(c *gc.C, id string) *environInstance { 230 base := s.NewBaseInstance(c, id) 231 return newInstance(base, s.Env) 232 } 233 234 type BaseSuite struct { 235 BaseSuiteUnpatched 236 237 FakeConn *fakeConn 238 FakeCommon *fakeCommon 239 FakeEnviron *fakeEnviron 240 FakeImages *fakeImages 241 } 242 243 func (s *BaseSuite) SetUpTest(c *gc.C) { 244 s.BaseSuiteUnpatched.SetUpTest(c) 245 246 s.FakeConn = &fakeConn{} 247 s.FakeCommon = &fakeCommon{} 248 s.FakeEnviron = &fakeEnviron{} 249 s.FakeImages = &fakeImages{} 250 251 // Patch out all expensive external deps. 252 s.Env.gce = s.FakeConn 253 s.PatchValue(&newConnection, func(*environConfig) (gceConnection, error) { 254 return s.FakeConn, nil 255 }) 256 s.PatchValue(&supportedArchitectures, s.FakeCommon.SupportedArchitectures) 257 s.PatchValue(&bootstrap, s.FakeCommon.Bootstrap) 258 s.PatchValue(&destroyEnv, s.FakeCommon.Destroy) 259 s.PatchValue(&availabilityZoneAllocations, s.FakeCommon.AvailabilityZoneAllocations) 260 s.PatchValue(&buildInstanceSpec, s.FakeEnviron.BuildInstanceSpec) 261 s.PatchValue(&getHardwareCharacteristics, s.FakeEnviron.GetHardwareCharacteristics) 262 s.PatchValue(&newRawInstance, s.FakeEnviron.NewRawInstance) 263 s.PatchValue(&findInstanceSpec, s.FakeEnviron.FindInstanceSpec) 264 s.PatchValue(&getInstances, s.FakeEnviron.GetInstances) 265 s.PatchValue(&imageMetadataFetch, s.FakeImages.ImageMetadataFetch) 266 } 267 268 func (s *BaseSuite) CheckNoAPI(c *gc.C) { 269 c.Check(s.FakeConn.Calls, gc.HasLen, 0) 270 } 271 272 // TODO(ericsnow) Move fakeCallArgs, fakeCall, and fake to the testing repo? 273 274 type FakeCallArgs map[string]interface{} 275 276 type FakeCall struct { 277 FuncName string 278 Args FakeCallArgs 279 } 280 281 type fake struct { 282 calls []FakeCall 283 284 Err error 285 FailOnCall int 286 } 287 288 func (f *fake) err() error { 289 if len(f.calls) != f.FailOnCall+1 { 290 return nil 291 } 292 return f.Err 293 } 294 295 func (f *fake) addCall(funcName string, args FakeCallArgs) { 296 f.calls = append(f.calls, FakeCall{ 297 FuncName: funcName, 298 Args: args, 299 }) 300 } 301 302 func (f *fake) CheckCalls(c *gc.C, expected []FakeCall) { 303 c.Check(f.calls, jc.DeepEquals, expected) 304 } 305 306 type fakeCommon struct { 307 fake 308 309 Arches []string 310 Arch string 311 Series string 312 BSFinalizer environs.BootstrapFinalizer 313 AZInstances []common.AvailabilityZoneInstances 314 } 315 316 func (fc *fakeCommon) SupportedArchitectures(env environs.Environ, cons *imagemetadata.ImageConstraint) ([]string, error) { 317 fc.addCall("SupportedArchitectures", FakeCallArgs{ 318 "env": env, 319 "cons": cons, 320 }) 321 return fc.Arches, fc.err() 322 } 323 324 func (fc *fakeCommon) Bootstrap(ctx environs.BootstrapContext, env environs.Environ, params environs.BootstrapParams) (string, string, environs.BootstrapFinalizer, error) { 325 fc.addCall("Bootstrap", FakeCallArgs{ 326 "ctx": ctx, 327 "env": env, 328 "params": params, 329 }) 330 return fc.Arch, fc.Series, fc.BSFinalizer, fc.err() 331 } 332 333 func (fc *fakeCommon) Destroy(env environs.Environ) error { 334 fc.addCall("Destroy", FakeCallArgs{ 335 "env": env, 336 }) 337 return fc.err() 338 } 339 340 func (fc *fakeCommon) AvailabilityZoneAllocations(env common.ZonedEnviron, group []instance.Id) ([]common.AvailabilityZoneInstances, error) { 341 fc.addCall("AvailabilityZoneAllocations", FakeCallArgs{ 342 "env": env, 343 "group": group, 344 }) 345 return fc.AZInstances, fc.err() 346 } 347 348 type fakeEnviron struct { 349 fake 350 351 Inst *google.Instance 352 Insts []instance.Instance 353 Hwc *instance.HardwareCharacteristics 354 Spec *instances.InstanceSpec 355 } 356 357 func (fe *fakeEnviron) GetInstances(env *environ) ([]instance.Instance, error) { 358 fe.addCall("GetInstances", FakeCallArgs{ 359 "env": env, 360 }) 361 return fe.Insts, fe.err() 362 } 363 364 func (fe *fakeEnviron) BuildInstanceSpec(env *environ, args environs.StartInstanceParams) (*instances.InstanceSpec, error) { 365 fe.addCall("BuildInstanceSpec", FakeCallArgs{ 366 "env": env, 367 "args": args, 368 }) 369 return fe.Spec, fe.err() 370 } 371 372 func (fe *fakeEnviron) GetHardwareCharacteristics(env *environ, spec *instances.InstanceSpec, inst *environInstance) *instance.HardwareCharacteristics { 373 fe.addCall("GetHardwareCharacteristics", FakeCallArgs{ 374 "env": env, 375 "spec": spec, 376 "inst": inst, 377 }) 378 return fe.Hwc 379 } 380 381 func (fe *fakeEnviron) NewRawInstance(env *environ, args environs.StartInstanceParams, spec *instances.InstanceSpec) (*google.Instance, error) { 382 fe.addCall("NewRawInstance", FakeCallArgs{ 383 "env": env, 384 "args": args, 385 "spec": spec, 386 }) 387 return fe.Inst, fe.err() 388 } 389 390 func (fe *fakeEnviron) FindInstanceSpec(env *environ, stream string, ic *instances.InstanceConstraint) (*instances.InstanceSpec, error) { 391 fe.addCall("FindInstanceSpec", FakeCallArgs{ 392 "env": env, 393 "stream": stream, 394 "ic": ic, 395 }) 396 return fe.Spec, fe.err() 397 } 398 399 type fakeImages struct { 400 fake 401 402 Metadata []*imagemetadata.ImageMetadata 403 ResolveInfo *simplestreams.ResolveInfo 404 } 405 406 func (fi *fakeImages) ImageMetadataFetch(sources []simplestreams.DataSource, cons *imagemetadata.ImageConstraint, onlySigned bool) ([]*imagemetadata.ImageMetadata, *simplestreams.ResolveInfo, error) { 407 return fi.Metadata, fi.ResolveInfo, fi.err() 408 } 409 410 // TODO(ericsnow) Refactor fakeConnCall and fakeConn to embed fakeCall and fake. 411 412 type fakeConnCall struct { 413 FuncName string 414 415 ID string 416 IDs []string 417 ZoneName string 418 ZoneNames []string 419 Prefix string 420 Statuses []string 421 InstanceSpec google.InstanceSpec 422 FirewallName string 423 PortRanges []network.PortRange 424 Region string 425 } 426 427 type fakeConn struct { 428 Calls []fakeConnCall 429 430 Inst *google.Instance 431 Insts []google.Instance 432 PortRanges []network.PortRange 433 Zones []google.AvailabilityZone 434 Err error 435 FailOnCall int 436 } 437 438 func (fc *fakeConn) err() error { 439 if len(fc.Calls) != fc.FailOnCall+1 { 440 return nil 441 } 442 return fc.Err 443 } 444 445 func (fc *fakeConn) VerifyCredentials() error { 446 fc.Calls = append(fc.Calls, fakeConnCall{ 447 FuncName: "", 448 }) 449 return fc.err() 450 } 451 452 func (fc *fakeConn) Instance(id, zone string) (google.Instance, error) { 453 fc.Calls = append(fc.Calls, fakeConnCall{ 454 FuncName: "Instance", 455 ID: id, 456 ZoneName: zone, 457 }) 458 return *fc.Inst, fc.err() 459 } 460 461 func (fc *fakeConn) Instances(prefix string, statuses ...string) ([]google.Instance, error) { 462 fc.Calls = append(fc.Calls, fakeConnCall{ 463 FuncName: "Instances", 464 Prefix: prefix, 465 Statuses: statuses, 466 }) 467 return fc.Insts, fc.err() 468 } 469 470 func (fc *fakeConn) AddInstance(spec google.InstanceSpec, zones ...string) (*google.Instance, error) { 471 fc.Calls = append(fc.Calls, fakeConnCall{ 472 FuncName: "AddInstance", 473 InstanceSpec: spec, 474 ZoneNames: zones, 475 }) 476 return fc.Inst, fc.err() 477 } 478 479 func (fc *fakeConn) RemoveInstances(prefix string, ids ...string) error { 480 fc.Calls = append(fc.Calls, fakeConnCall{ 481 FuncName: "RemoveInstances", 482 Prefix: prefix, 483 IDs: ids, 484 }) 485 return fc.err() 486 } 487 488 func (fc *fakeConn) Ports(fwname string) ([]network.PortRange, error) { 489 fc.Calls = append(fc.Calls, fakeConnCall{ 490 FuncName: "Ports", 491 FirewallName: fwname, 492 }) 493 return fc.PortRanges, fc.err() 494 } 495 496 func (fc *fakeConn) OpenPorts(fwname string, ports ...network.PortRange) error { 497 fc.Calls = append(fc.Calls, fakeConnCall{ 498 FuncName: "OpenPorts", 499 FirewallName: fwname, 500 PortRanges: ports, 501 }) 502 return fc.err() 503 } 504 505 func (fc *fakeConn) ClosePorts(fwname string, ports ...network.PortRange) error { 506 fc.Calls = append(fc.Calls, fakeConnCall{ 507 FuncName: "ClosePorts", 508 FirewallName: fwname, 509 PortRanges: ports, 510 }) 511 return fc.err() 512 } 513 514 func (fc *fakeConn) AvailabilityZones(region string) ([]google.AvailabilityZone, error) { 515 fc.Calls = append(fc.Calls, fakeConnCall{ 516 FuncName: "AvailabilityZones", 517 Region: region, 518 }) 519 return fc.Zones, fc.err() 520 } 521 522 func (fc *fakeConn) WasCalled(funcName string) (bool, []fakeConnCall) { 523 var calls []fakeConnCall 524 called := false 525 for _, call := range fc.Calls { 526 if call.FuncName == funcName { 527 called = true 528 calls = append(calls, call) 529 } 530 } 531 return called, calls 532 }