LEFT | RIGHT |
(no file at all) | |
1 // Copyright 2011, 2012, 2013 Canonical Ltd. | 1 // Copyright 2011, 2012, 2013 Canonical Ltd. |
2 // Licensed under the AGPLv3, see LICENCE file for details. | 2 // Licensed under the AGPLv3, see LICENCE file for details. |
3 | 3 |
4 package ec2 | 4 package ec2 |
5 | 5 |
6 import ( | 6 import ( |
7 "fmt" | 7 "fmt" |
8 "io/ioutil" | 8 "io/ioutil" |
9 "launchpad.net/goamz/aws" | 9 "launchpad.net/goamz/aws" |
10 "launchpad.net/goamz/ec2" | 10 "launchpad.net/goamz/ec2" |
11 "launchpad.net/goamz/s3" | 11 "launchpad.net/goamz/s3" |
12 "launchpad.net/juju-core/constraints" | 12 "launchpad.net/juju-core/constraints" |
13 "launchpad.net/juju-core/environs" | 13 "launchpad.net/juju-core/environs" |
14 "launchpad.net/juju-core/environs/cloudinit" | 14 "launchpad.net/juju-core/environs/cloudinit" |
15 "launchpad.net/juju-core/environs/config" | 15 "launchpad.net/juju-core/environs/config" |
16 "launchpad.net/juju-core/environs/imagemetadata" | 16 "launchpad.net/juju-core/environs/imagemetadata" |
17 "launchpad.net/juju-core/environs/instances" | 17 "launchpad.net/juju-core/environs/instances" |
18 "launchpad.net/juju-core/environs/tools" | 18 "launchpad.net/juju-core/environs/tools" |
19 "launchpad.net/juju-core/errors" | 19 "launchpad.net/juju-core/errors" |
| 20 "launchpad.net/juju-core/instance" |
20 "launchpad.net/juju-core/log" | 21 "launchpad.net/juju-core/log" |
21 "launchpad.net/juju-core/state" | 22 "launchpad.net/juju-core/state" |
22 "launchpad.net/juju-core/state/api" | 23 "launchpad.net/juju-core/state/api" |
23 "launchpad.net/juju-core/state/api/params" | |
24 "launchpad.net/juju-core/utils" | 24 "launchpad.net/juju-core/utils" |
25 "net/http" | 25 "net/http" |
26 "strings" | 26 "strings" |
27 "sync" | 27 "sync" |
28 "time" | 28 "time" |
29 ) | 29 ) |
30 | 30 |
31 // A request may fail to due "eventual consistency" semantics, which | 31 // A request may fail to due "eventual consistency" semantics, which |
32 // should resolve fairly quickly. A request may also fail due to a slow | 32 // should resolve fairly quickly. A request may also fail due to a slow |
33 // state transition (for instance an instance taking a while to release | 33 // state transition (for instance an instance taking a while to release |
(...skipping 24 matching lines...) Expand all Loading... |
58 ecfgMutex sync.Mutex | 58 ecfgMutex sync.Mutex |
59 ecfgUnlocked *environConfig | 59 ecfgUnlocked *environConfig |
60 ec2Unlocked *ec2.EC2 | 60 ec2Unlocked *ec2.EC2 |
61 s3Unlocked *s3.S3 | 61 s3Unlocked *s3.S3 |
62 storageUnlocked *storage | 62 storageUnlocked *storage |
63 publicStorageUnlocked *storage // optional. | 63 publicStorageUnlocked *storage // optional. |
64 } | 64 } |
65 | 65 |
66 var _ environs.Environ = (*environ)(nil) | 66 var _ environs.Environ = (*environ)(nil) |
67 | 67 |
68 type instance struct { | 68 type ec2Instance struct { |
69 e *environ | 69 e *environ |
70 *ec2.Instance | 70 *ec2.Instance |
71 } | 71 » arch *string |
72 | 72 » instType *instances.InstanceType |
73 func (inst *instance) String() string { | 73 } |
| 74 |
| 75 func (inst *ec2Instance) String() string { |
74 return inst.InstanceId | 76 return inst.InstanceId |
75 } | 77 } |
76 | 78 |
77 var _ environs.Instance = (*instance)(nil) | 79 var _ instance.Instance = (*ec2Instance)(nil) |
78 | 80 |
79 func (inst *instance) Id() state.InstanceId { | 81 func (inst *ec2Instance) Id() instance.Id { |
80 » return state.InstanceId(inst.InstanceId) | 82 » return instance.Id(inst.InstanceId) |
81 } | 83 } |
82 | 84 |
83 func (inst *instance) DNSName() (string, error) { | 85 func (inst *ec2Instance) Metadata() *instance.Metadata { |
| 86 » metadata := &instance.Metadata{Arch: inst.arch} |
| 87 » if inst.instType != nil { |
| 88 » » metadata.Mem = &inst.instType.Mem |
| 89 » » metadata.CpuCores = &inst.instType.CpuCores |
| 90 » » metadata.CpuPower = inst.instType.CpuPower |
| 91 » } |
| 92 » return metadata |
| 93 } |
| 94 |
| 95 func (inst *ec2Instance) DNSName() (string, error) { |
84 if inst.Instance.DNSName != "" { | 96 if inst.Instance.DNSName != "" { |
85 return inst.Instance.DNSName, nil | 97 return inst.Instance.DNSName, nil |
86 } | 98 } |
87 // Fetch the instance information again, in case | 99 // Fetch the instance information again, in case |
88 // the DNS information has become available. | 100 // the DNS information has become available. |
89 » insts, err := inst.e.Instances([]state.InstanceId{inst.Id()}) | 101 » insts, err := inst.e.Instances([]instance.Id{inst.Id()}) |
90 if err != nil { | 102 if err != nil { |
91 return "", err | 103 return "", err |
92 } | 104 } |
93 » freshInst := insts[0].(*instance).Instance | 105 » freshInst := insts[0].(*ec2Instance).Instance |
94 if freshInst.DNSName == "" { | 106 if freshInst.DNSName == "" { |
95 » » return "", environs.ErrNoDNSName | 107 » » return "", instance.ErrNoDNSName |
96 } | 108 } |
97 inst.Instance.DNSName = freshInst.DNSName | 109 inst.Instance.DNSName = freshInst.DNSName |
98 return freshInst.DNSName, nil | 110 return freshInst.DNSName, nil |
99 } | 111 } |
100 | 112 |
101 func (inst *instance) WaitDNSName() (string, error) { | 113 func (inst *ec2Instance) WaitDNSName() (string, error) { |
102 for a := longAttempt.Start(); a.Next(); { | 114 for a := longAttempt.Start(); a.Next(); { |
103 name, err := inst.DNSName() | 115 name, err := inst.DNSName() |
104 » » if err == nil || err != environs.ErrNoDNSName { | 116 » » if err == nil || err != instance.ErrNoDNSName { |
105 return name, err | 117 return name, err |
106 } | 118 } |
107 } | 119 } |
108 return "", fmt.Errorf("timed out trying to get DNS address for %v", inst
.Id()) | 120 return "", fmt.Errorf("timed out trying to get DNS address for %v", inst
.Id()) |
109 } | 121 } |
110 | 122 |
111 func (p environProvider) BoilerplateConfig() string { | 123 func (p environProvider) BoilerplateConfig() string { |
112 return ` | 124 return ` |
113 ## https://juju.ubuntu.com/get-started/amazon/ | 125 ## https://juju.ubuntu.com/get-started/amazon/ |
114 amazon: | 126 amazon: |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
150 } | 162 } |
151 | 163 |
152 func (environProvider) PublicAddress() (string, error) { | 164 func (environProvider) PublicAddress() (string, error) { |
153 return fetchMetadata("public-hostname") | 165 return fetchMetadata("public-hostname") |
154 } | 166 } |
155 | 167 |
156 func (environProvider) PrivateAddress() (string, error) { | 168 func (environProvider) PrivateAddress() (string, error) { |
157 return fetchMetadata("local-hostname") | 169 return fetchMetadata("local-hostname") |
158 } | 170 } |
159 | 171 |
160 func (environProvider) InstanceId() (state.InstanceId, error) { | 172 func (environProvider) InstanceId() (instance.Id, error) { |
161 str, err := fetchMetadata("instance-id") | 173 str, err := fetchMetadata("instance-id") |
162 » return state.InstanceId(str), err | 174 » return instance.Id(str), err |
163 } | 175 } |
164 | 176 |
165 func (e *environ) Config() *config.Config { | 177 func (e *environ) Config() *config.Config { |
166 return e.ecfg().Config | 178 return e.ecfg().Config |
167 } | 179 } |
168 | 180 |
169 func (e *environ) SetConfig(cfg *config.Config) error { | 181 func (e *environ) SetConfig(cfg *config.Config) error { |
170 ecfg, err := providerInstance.newConfig(cfg) | 182 ecfg, err := providerInstance.newConfig(cfg) |
171 if err != nil { | 183 if err != nil { |
172 return err | 184 return err |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
250 break | 262 break |
251 } | 263 } |
252 } | 264 } |
253 if err == nil { | 265 if err == nil { |
254 return fmt.Errorf("environment is already bootstrapped") | 266 return fmt.Errorf("environment is already bootstrapped") |
255 } | 267 } |
256 if !errors.IsNotFoundError(err) { | 268 if !errors.IsNotFoundError(err) { |
257 return fmt.Errorf("cannot query old bootstrap state: %v", err) | 269 return fmt.Errorf("cannot query old bootstrap state: %v", err) |
258 } | 270 } |
259 | 271 |
| 272 err = environs.VerifyStorage(e.Storage()) |
| 273 if err != nil { |
| 274 return err |
| 275 } |
| 276 |
260 possibleTools, err := environs.FindBootstrapTools(e, cons) | 277 possibleTools, err := environs.FindBootstrapTools(e, cons) |
261 if err != nil { | 278 if err != nil { |
262 return err | 279 return err |
263 } | 280 } |
264 inst, err := e.startInstance(&startInstanceParams{ | 281 inst, err := e.startInstance(&startInstanceParams{ |
265 machineId: "0", | 282 machineId: "0", |
266 machineNonce: state.BootstrapNonce, | 283 machineNonce: state.BootstrapNonce, |
267 series: e.Config().DefaultSeries(), | 284 series: e.Config().DefaultSeries(), |
268 constraints: cons, | 285 constraints: cons, |
269 possibleTools: possibleTools, | 286 possibleTools: possibleTools, |
270 stateServer: true, | 287 stateServer: true, |
271 }) | 288 }) |
272 if err != nil { | 289 if err != nil { |
273 return fmt.Errorf("cannot start bootstrap instance: %v", err) | 290 return fmt.Errorf("cannot start bootstrap instance: %v", err) |
274 } | 291 } |
275 err = e.saveState(&bootstrapState{ | 292 err = e.saveState(&bootstrapState{ |
276 » » StateInstances: []state.InstanceId{inst.Id()}, | 293 » » StateInstances: []instance.Id{inst.Id()}, |
277 }) | 294 }) |
278 if err != nil { | 295 if err != nil { |
279 // ignore error on StopInstance because the previous error is | 296 // ignore error on StopInstance because the previous error is |
280 // more important. | 297 // more important. |
281 » » e.StopInstances([]environs.Instance{inst}) | 298 » » e.StopInstances([]instance.Instance{inst}) |
282 return fmt.Errorf("cannot save state: %v", err) | 299 return fmt.Errorf("cannot save state: %v", err) |
283 } | 300 } |
284 // TODO make safe in the case of racing Bootstraps | 301 // TODO make safe in the case of racing Bootstraps |
285 // If two Bootstraps are called concurrently, there's | 302 // If two Bootstraps are called concurrently, there's |
286 // no way to use S3 to make sure that only one succeeds. | 303 // no way to use S3 to make sure that only one succeeds. |
287 // Perhaps consider using SimpleDB for state storage | 304 // Perhaps consider using SimpleDB for state storage |
288 // which would enable that possibility. | 305 // which would enable that possibility. |
289 return nil | 306 return nil |
290 } | 307 } |
291 | 308 |
(...skipping 14 matching lines...) Expand all Loading... |
306 log.Infof("environs/ec2: waiting for DNS name(s) of state server instanc
es %v", st.StateInstances) | 323 log.Infof("environs/ec2: waiting for DNS name(s) of state server instanc
es %v", st.StateInstances) |
307 for a := longAttempt.Start(); len(stateAddrs) == 0 && a.Next(); { | 324 for a := longAttempt.Start(); len(stateAddrs) == 0 && a.Next(); { |
308 insts, err := e.Instances(st.StateInstances) | 325 insts, err := e.Instances(st.StateInstances) |
309 if err != nil && err != environs.ErrPartialInstances { | 326 if err != nil && err != environs.ErrPartialInstances { |
310 return nil, nil, err | 327 return nil, nil, err |
311 } | 328 } |
312 for _, inst := range insts { | 329 for _, inst := range insts { |
313 if inst == nil { | 330 if inst == nil { |
314 continue | 331 continue |
315 } | 332 } |
316 » » » name := inst.(*instance).Instance.DNSName | 333 » » » name := inst.(*ec2Instance).Instance.DNSName |
317 if name != "" { | 334 if name != "" { |
318 statePortSuffix := fmt.Sprintf(":%d", config.Sta
tePort()) | 335 statePortSuffix := fmt.Sprintf(":%d", config.Sta
tePort()) |
319 apiPortSuffix := fmt.Sprintf(":%d", config.APIPo
rt()) | 336 apiPortSuffix := fmt.Sprintf(":%d", config.APIPo
rt()) |
320 stateAddrs = append(stateAddrs, name+statePortSu
ffix) | 337 stateAddrs = append(stateAddrs, name+statePortSu
ffix) |
321 apiAddrs = append(apiAddrs, name+apiPortSuffix) | 338 apiAddrs = append(apiAddrs, name+apiPortSuffix) |
322 } | 339 } |
323 } | 340 } |
324 } | 341 } |
325 if len(stateAddrs) == 0 { | 342 if len(stateAddrs) == 0 { |
326 return nil, nil, fmt.Errorf("timed out waiting for mgo address f
rom %v", st.StateInstances) | 343 return nil, nil, fmt.Errorf("timed out waiting for mgo address f
rom %v", st.StateInstances) |
327 } | 344 } |
328 return &state.Info{ | 345 return &state.Info{ |
329 Addrs: stateAddrs, | 346 Addrs: stateAddrs, |
330 CACert: cert, | 347 CACert: cert, |
331 }, &api.Info{ | 348 }, &api.Info{ |
332 Addrs: apiAddrs, | 349 Addrs: apiAddrs, |
333 CACert: cert, | 350 CACert: cert, |
334 }, nil | 351 }, nil |
335 } | 352 } |
336 | 353 |
337 // getImageBaseURLs returns a list of URLs which are used to search for simplest
reams image metadata. | 354 // getImageBaseURLs returns a list of URLs which are used to search for simplest
reams image metadata. |
338 func (e *environ) getImageBaseURLs() ([]string, error) { | 355 func (e *environ) getImageBaseURLs() ([]string, error) { |
339 // Use the default simplestreams base URL. | 356 // Use the default simplestreams base URL. |
340 return []string{imagemetadata.DefaultBaseURL}, nil | 357 return []string{imagemetadata.DefaultBaseURL}, nil |
341 } | 358 } |
342 | 359 |
343 func (e *environ) StartInstance(machineId, machineNonce string, series string, c
ons constraints.Value, info *state.Info, apiInfo *api.Info) (environs.Instance,
error) { | 360 func (e *environ) StartInstance(machineId, machineNonce string, series string, c
ons constraints.Value, info *state.Info, apiInfo *api.Info) (instance.Instance,
error) { |
344 possibleTools, err := environs.FindInstanceTools(e, series, cons) | 361 possibleTools, err := environs.FindInstanceTools(e, series, cons) |
345 if err != nil { | 362 if err != nil { |
346 return nil, err | 363 return nil, err |
347 } | 364 } |
348 return e.startInstance(&startInstanceParams{ | 365 return e.startInstance(&startInstanceParams{ |
349 machineId: machineId, | 366 machineId: machineId, |
350 machineNonce: machineNonce, | 367 machineNonce: machineNonce, |
351 series: series, | 368 series: series, |
352 constraints: cons, | 369 constraints: cons, |
353 info: info, | 370 info: info, |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
390 info *state.Info | 407 info *state.Info |
391 apiInfo *api.Info | 408 apiInfo *api.Info |
392 possibleTools tools.List | 409 possibleTools tools.List |
393 stateServer bool | 410 stateServer bool |
394 } | 411 } |
395 | 412 |
396 const ebsStorage = "ebs" | 413 const ebsStorage = "ebs" |
397 | 414 |
398 // startInstance is the internal version of StartInstance, used by Bootstrap | 415 // startInstance is the internal version of StartInstance, used by Bootstrap |
399 // as well as via StartInstance itself. | 416 // as well as via StartInstance itself. |
400 func (e *environ) startInstance(scfg *startInstanceParams) (environs.Instance, e
rror) { | 417 func (e *environ) startInstance(scfg *startInstanceParams) (instance.Instance, e
rror) { |
401 series := scfg.possibleTools.Series() | 418 series := scfg.possibleTools.Series() |
402 if len(series) != 1 { | 419 if len(series) != 1 { |
403 return nil, fmt.Errorf("expected single series, got %v", series) | 420 return nil, fmt.Errorf("expected single series, got %v", series) |
404 } | 421 } |
405 if series[0] != scfg.series { | 422 if series[0] != scfg.series { |
406 return nil, fmt.Errorf("tools mismatch: expected series %v, got
%v", series, series[0]) | 423 return nil, fmt.Errorf("tools mismatch: expected series %v, got
%v", series, series[0]) |
407 } | 424 } |
408 arches := scfg.possibleTools.Arches() | 425 arches := scfg.possibleTools.Arches() |
409 storage := ebsStorage | 426 storage := ebsStorage |
410 baseURLs, err := e.getImageBaseURLs() | 427 baseURLs, err := e.getImageBaseURLs() |
(...skipping 24 matching lines...) Expand all Loading... |
435 return nil, fmt.Errorf("cannot set up groups: %v", err) | 452 return nil, fmt.Errorf("cannot set up groups: %v", err) |
436 } | 453 } |
437 var instances *ec2.RunInstancesResp | 454 var instances *ec2.RunInstancesResp |
438 | 455 |
439 for a := shortAttempt.Start(); a.Next(); { | 456 for a := shortAttempt.Start(); a.Next(); { |
440 instances, err = e.ec2().RunInstances(&ec2.RunInstances{ | 457 instances, err = e.ec2().RunInstances(&ec2.RunInstances{ |
441 ImageId: spec.Image.Id, | 458 ImageId: spec.Image.Id, |
442 MinCount: 1, | 459 MinCount: 1, |
443 MaxCount: 1, | 460 MaxCount: 1, |
444 UserData: userData, | 461 UserData: userData, |
445 » » » InstanceType: spec.InstanceTypeName, | 462 » » » InstanceType: spec.InstanceType.Name, |
446 SecurityGroups: groups, | 463 SecurityGroups: groups, |
447 }) | 464 }) |
448 if err == nil || ec2ErrCode(err) != "InvalidGroup.NotFound" { | 465 if err == nil || ec2ErrCode(err) != "InvalidGroup.NotFound" { |
449 break | 466 break |
450 } | 467 } |
451 } | 468 } |
452 if err != nil { | 469 if err != nil { |
453 return nil, fmt.Errorf("cannot run instances: %v", err) | 470 return nil, fmt.Errorf("cannot run instances: %v", err) |
454 } | 471 } |
455 if len(instances.Instances) != 1 { | 472 if len(instances.Instances) != 1 { |
456 return nil, fmt.Errorf("expected 1 started instance, got %d", le
n(instances.Instances)) | 473 return nil, fmt.Errorf("expected 1 started instance, got %d", le
n(instances.Instances)) |
457 } | 474 } |
458 » inst := &instance{e, &instances.Instances[0]} | 475 » inst := &ec2Instance{ |
| 476 » » e: e, |
| 477 » » Instance: &instances.Instances[0], |
| 478 » » arch: &spec.Image.Arch, |
| 479 » » instType: &spec.InstanceType, |
| 480 » } |
459 log.Infof("environs/ec2: started instance %q", inst.Id()) | 481 log.Infof("environs/ec2: started instance %q", inst.Id()) |
460 return inst, nil | 482 return inst, nil |
461 } | 483 } |
462 | 484 |
463 func (e *environ) StopInstances(insts []environs.Instance) error { | 485 func (e *environ) StopInstances(insts []instance.Instance) error { |
464 » ids := make([]state.InstanceId, len(insts)) | 486 » ids := make([]instance.Id, len(insts)) |
465 for i, inst := range insts { | 487 for i, inst := range insts { |
466 » » ids[i] = inst.(*instance).Id() | 488 » » ids[i] = inst.(*ec2Instance).Id() |
467 } | 489 } |
468 return e.terminateInstances(ids) | 490 return e.terminateInstances(ids) |
469 } | 491 } |
470 | 492 |
471 // gatherInstances tries to get information on each instance | 493 // gatherInstances tries to get information on each instance |
472 // id whose corresponding insts slot is nil. | 494 // id whose corresponding insts slot is nil. |
473 // It returns environs.ErrPartialInstances if the insts | 495 // It returns environs.ErrPartialInstances if the insts |
474 // slice has not been completely filled. | 496 // slice has not been completely filled. |
475 func (e *environ) gatherInstances(ids []state.InstanceId, insts []environs.Insta
nce) error { | 497 func (e *environ) gatherInstances(ids []instance.Id, insts []instance.Instance)
error { |
476 var need []string | 498 var need []string |
477 for i, inst := range insts { | 499 for i, inst := range insts { |
478 if inst == nil { | 500 if inst == nil { |
479 need = append(need, string(ids[i])) | 501 need = append(need, string(ids[i])) |
480 } | 502 } |
481 } | 503 } |
482 if len(need) == 0 { | 504 if len(need) == 0 { |
483 return nil | 505 return nil |
484 } | 506 } |
485 filter := ec2.NewFilter() | 507 filter := ec2.NewFilter() |
486 filter.Add("instance-state-name", "pending", "running") | 508 filter.Add("instance-state-name", "pending", "running") |
487 filter.Add("group-name", e.jujuGroupName()) | 509 filter.Add("group-name", e.jujuGroupName()) |
488 filter.Add("instance-id", need...) | 510 filter.Add("instance-id", need...) |
489 resp, err := e.ec2().Instances(nil, filter) | 511 resp, err := e.ec2().Instances(nil, filter) |
490 if err != nil { | 512 if err != nil { |
491 return err | 513 return err |
492 } | 514 } |
493 n := 0 | 515 n := 0 |
494 // For each requested id, add it to the returned instances | 516 // For each requested id, add it to the returned instances |
495 // if we find it in the response. | 517 // if we find it in the response. |
496 for i, id := range ids { | 518 for i, id := range ids { |
497 if insts[i] != nil { | 519 if insts[i] != nil { |
498 continue | 520 continue |
499 } | 521 } |
500 for j := range resp.Reservations { | 522 for j := range resp.Reservations { |
501 r := &resp.Reservations[j] | 523 r := &resp.Reservations[j] |
502 for k := range r.Instances { | 524 for k := range r.Instances { |
503 if r.Instances[k].InstanceId == string(id) { | 525 if r.Instances[k].InstanceId == string(id) { |
504 inst := r.Instances[k] | 526 inst := r.Instances[k] |
505 » » » » » insts[i] = &instance{e, &inst} | 527 » » » » » // TODO(wallyworld): lookup the details
to fill in the instance type data |
| 528 » » » » » insts[i] = &ec2Instance{e: e, Instance:
&inst} |
506 n++ | 529 n++ |
507 } | 530 } |
508 } | 531 } |
509 } | 532 } |
510 } | 533 } |
511 if n < len(ids) { | 534 if n < len(ids) { |
512 return environs.ErrPartialInstances | 535 return environs.ErrPartialInstances |
513 } | 536 } |
514 return nil | 537 return nil |
515 } | 538 } |
516 | 539 |
517 func (e *environ) Instances(ids []state.InstanceId) ([]environs.Instance, error)
{ | 540 func (e *environ) Instances(ids []instance.Id) ([]instance.Instance, error) { |
518 if len(ids) == 0 { | 541 if len(ids) == 0 { |
519 return nil, nil | 542 return nil, nil |
520 } | 543 } |
521 » insts := make([]environs.Instance, len(ids)) | 544 » insts := make([]instance.Instance, len(ids)) |
522 // Make a series of requests to cope with eventual consistency. | 545 // Make a series of requests to cope with eventual consistency. |
523 // Each request will attempt to add more instances to the requested | 546 // Each request will attempt to add more instances to the requested |
524 // set. | 547 // set. |
525 var err error | 548 var err error |
526 for a := shortAttempt.Start(); a.Next(); { | 549 for a := shortAttempt.Start(); a.Next(); { |
527 err = e.gatherInstances(ids, insts) | 550 err = e.gatherInstances(ids, insts) |
528 if err == nil || err != environs.ErrPartialInstances { | 551 if err == nil || err != environs.ErrPartialInstances { |
529 break | 552 break |
530 } | 553 } |
531 } | 554 } |
532 if err == environs.ErrPartialInstances { | 555 if err == environs.ErrPartialInstances { |
533 for _, inst := range insts { | 556 for _, inst := range insts { |
534 if inst != nil { | 557 if inst != nil { |
535 return insts, environs.ErrPartialInstances | 558 return insts, environs.ErrPartialInstances |
536 } | 559 } |
537 } | 560 } |
538 return nil, environs.ErrNoInstances | 561 return nil, environs.ErrNoInstances |
539 } | 562 } |
540 if err != nil { | 563 if err != nil { |
541 return nil, err | 564 return nil, err |
542 } | 565 } |
543 return insts, nil | 566 return insts, nil |
544 } | 567 } |
545 | 568 |
546 func (e *environ) AllInstances() ([]environs.Instance, error) { | 569 func (e *environ) AllInstances() ([]instance.Instance, error) { |
547 filter := ec2.NewFilter() | 570 filter := ec2.NewFilter() |
548 filter.Add("instance-state-name", "pending", "running") | 571 filter.Add("instance-state-name", "pending", "running") |
549 filter.Add("group-name", e.jujuGroupName()) | 572 filter.Add("group-name", e.jujuGroupName()) |
550 resp, err := e.ec2().Instances(nil, filter) | 573 resp, err := e.ec2().Instances(nil, filter) |
551 if err != nil { | 574 if err != nil { |
552 return nil, err | 575 return nil, err |
553 } | 576 } |
554 » var insts []environs.Instance | 577 » var insts []instance.Instance |
555 for _, r := range resp.Reservations { | 578 for _, r := range resp.Reservations { |
556 for i := range r.Instances { | 579 for i := range r.Instances { |
557 inst := r.Instances[i] | 580 inst := r.Instances[i] |
558 » » » insts = append(insts, &instance{e, &inst}) | 581 » » » // TODO(wallyworld): lookup the details to fill in the i
nstance type data |
| 582 » » » insts = append(insts, &ec2Instance{e: e, Instance: &inst
}) |
559 } | 583 } |
560 } | 584 } |
561 return insts, nil | 585 return insts, nil |
562 } | 586 } |
563 | 587 |
564 func (e *environ) Destroy(ensureInsts []environs.Instance) error { | 588 func (e *environ) Destroy(ensureInsts []instance.Instance) error { |
565 log.Infof("environs/ec2: destroying environment %q", e.name) | 589 log.Infof("environs/ec2: destroying environment %q", e.name) |
566 insts, err := e.AllInstances() | 590 insts, err := e.AllInstances() |
567 if err != nil { | 591 if err != nil { |
568 return fmt.Errorf("cannot get instances: %v", err) | 592 return fmt.Errorf("cannot get instances: %v", err) |
569 } | 593 } |
570 » found := make(map[state.InstanceId]bool) | 594 » found := make(map[instance.Id]bool) |
571 » var ids []state.InstanceId | 595 » var ids []instance.Id |
572 for _, inst := range insts { | 596 for _, inst := range insts { |
573 ids = append(ids, inst.Id()) | 597 ids = append(ids, inst.Id()) |
574 found[inst.Id()] = true | 598 found[inst.Id()] = true |
575 } | 599 } |
576 | 600 |
577 // Add any instances we've been told about but haven't yet shown | 601 // Add any instances we've been told about but haven't yet shown |
578 // up in the instance list. | 602 // up in the instance list. |
579 for _, inst := range ensureInsts { | 603 for _, inst := range ensureInsts { |
580 » » id := state.InstanceId(inst.(*instance).InstanceId) | 604 » » id := instance.Id(inst.(*ec2Instance).InstanceId) |
581 if !found[id] { | 605 if !found[id] { |
582 ids = append(ids, id) | 606 ids = append(ids, id) |
583 found[id] = true | 607 found[id] = true |
584 } | 608 } |
585 } | 609 } |
586 err = e.terminateInstances(ids) | 610 err = e.terminateInstances(ids) |
587 if err != nil { | 611 if err != nil { |
588 return err | 612 return err |
589 } | 613 } |
590 | 614 |
591 // To properly observe e.storageUnlocked we need to get its value while | 615 // To properly observe e.storageUnlocked we need to get its value while |
592 // holding e.ecfgMutex. e.Storage() does this for us, then we convert | 616 // holding e.ecfgMutex. e.Storage() does this for us, then we convert |
593 // back to the (*storage) to access the private deleteAll() method. | 617 // back to the (*storage) to access the private deleteAll() method. |
594 st := e.Storage().(*storage) | 618 st := e.Storage().(*storage) |
595 return st.deleteAll() | 619 return st.deleteAll() |
596 } | 620 } |
597 | 621 |
598 func portsToIPPerms(ports []params.Port) []ec2.IPPerm { | 622 func portsToIPPerms(ports []instance.Port) []ec2.IPPerm { |
599 ipPerms := make([]ec2.IPPerm, len(ports)) | 623 ipPerms := make([]ec2.IPPerm, len(ports)) |
600 for i, p := range ports { | 624 for i, p := range ports { |
601 ipPerms[i] = ec2.IPPerm{ | 625 ipPerms[i] = ec2.IPPerm{ |
602 Protocol: p.Protocol, | 626 Protocol: p.Protocol, |
603 FromPort: p.Number, | 627 FromPort: p.Number, |
604 ToPort: p.Number, | 628 ToPort: p.Number, |
605 SourceIPs: []string{"0.0.0.0/0"}, | 629 SourceIPs: []string{"0.0.0.0/0"}, |
606 } | 630 } |
607 } | 631 } |
608 return ipPerms | 632 return ipPerms |
609 } | 633 } |
610 | 634 |
611 func (e *environ) openPortsInGroup(name string, ports []params.Port) error { | 635 func (e *environ) openPortsInGroup(name string, ports []instance.Port) error { |
612 if len(ports) == 0 { | 636 if len(ports) == 0 { |
613 return nil | 637 return nil |
614 } | 638 } |
615 // Give permissions for anyone to access the given ports. | 639 // Give permissions for anyone to access the given ports. |
616 ipPerms := portsToIPPerms(ports) | 640 ipPerms := portsToIPPerms(ports) |
617 g := ec2.SecurityGroup{Name: name} | 641 g := ec2.SecurityGroup{Name: name} |
618 _, err := e.ec2().AuthorizeSecurityGroup(g, ipPerms) | 642 _, err := e.ec2().AuthorizeSecurityGroup(g, ipPerms) |
619 if err != nil && ec2ErrCode(err) == "InvalidPermission.Duplicate" { | 643 if err != nil && ec2ErrCode(err) == "InvalidPermission.Duplicate" { |
620 if len(ports) == 1 { | 644 if len(ports) == 1 { |
621 return nil | 645 return nil |
622 } | 646 } |
623 // If there's more than one port and we get a duplicate error, | 647 // If there's more than one port and we get a duplicate error, |
624 // then we go through authorizing each port individually, | 648 // then we go through authorizing each port individually, |
625 // otherwise the ports that were *not* duplicates will have | 649 // otherwise the ports that were *not* duplicates will have |
626 // been ignored | 650 // been ignored |
627 for i := range ipPerms { | 651 for i := range ipPerms { |
628 _, err := e.ec2().AuthorizeSecurityGroup(g, ipPerms[i:i+
1]) | 652 _, err := e.ec2().AuthorizeSecurityGroup(g, ipPerms[i:i+
1]) |
629 if err != nil && ec2ErrCode(err) != "InvalidPermission.D
uplicate" { | 653 if err != nil && ec2ErrCode(err) != "InvalidPermission.D
uplicate" { |
630 return fmt.Errorf("cannot open port %v: %v", ipP
erms[i], err) | 654 return fmt.Errorf("cannot open port %v: %v", ipP
erms[i], err) |
631 } | 655 } |
632 } | 656 } |
633 return nil | 657 return nil |
634 } | 658 } |
635 if err != nil { | 659 if err != nil { |
636 return fmt.Errorf("cannot open ports: %v", err) | 660 return fmt.Errorf("cannot open ports: %v", err) |
637 } | 661 } |
638 return nil | 662 return nil |
639 } | 663 } |
640 | 664 |
641 func (e *environ) closePortsInGroup(name string, ports []params.Port) error { | 665 func (e *environ) closePortsInGroup(name string, ports []instance.Port) error { |
642 if len(ports) == 0 { | 666 if len(ports) == 0 { |
643 return nil | 667 return nil |
644 } | 668 } |
645 // Revoke permissions for anyone to access the given ports. | 669 // Revoke permissions for anyone to access the given ports. |
646 // Note that ec2 allows the revocation of permissions that aren't | 670 // Note that ec2 allows the revocation of permissions that aren't |
647 // granted, so this is naturally idempotent. | 671 // granted, so this is naturally idempotent. |
648 g := ec2.SecurityGroup{Name: name} | 672 g := ec2.SecurityGroup{Name: name} |
649 _, err := e.ec2().RevokeSecurityGroup(g, portsToIPPerms(ports)) | 673 _, err := e.ec2().RevokeSecurityGroup(g, portsToIPPerms(ports)) |
650 if err != nil { | 674 if err != nil { |
651 return fmt.Errorf("cannot close ports: %v", err) | 675 return fmt.Errorf("cannot close ports: %v", err) |
652 } | 676 } |
653 return nil | 677 return nil |
654 } | 678 } |
655 | 679 |
656 func (e *environ) portsInGroup(name string) (ports []params.Port, err error) { | 680 func (e *environ) portsInGroup(name string) (ports []instance.Port, err error) { |
657 g := ec2.SecurityGroup{Name: name} | 681 g := ec2.SecurityGroup{Name: name} |
658 resp, err := e.ec2().SecurityGroups([]ec2.SecurityGroup{g}, nil) | 682 resp, err := e.ec2().SecurityGroups([]ec2.SecurityGroup{g}, nil) |
659 if err != nil { | 683 if err != nil { |
660 return nil, err | 684 return nil, err |
661 } | 685 } |
662 if len(resp.Groups) != 1 { | 686 if len(resp.Groups) != 1 { |
663 return nil, fmt.Errorf("expected one security group, got %d", le
n(resp.Groups)) | 687 return nil, fmt.Errorf("expected one security group, got %d", le
n(resp.Groups)) |
664 } | 688 } |
665 for _, p := range resp.Groups[0].IPPerms { | 689 for _, p := range resp.Groups[0].IPPerms { |
666 if len(p.SourceIPs) != 1 { | 690 if len(p.SourceIPs) != 1 { |
667 log.Warningf("environs/ec2: unexpected IP permission fou
nd: %v", p) | 691 log.Warningf("environs/ec2: unexpected IP permission fou
nd: %v", p) |
668 continue | 692 continue |
669 } | 693 } |
670 for i := p.FromPort; i <= p.ToPort; i++ { | 694 for i := p.FromPort; i <= p.ToPort; i++ { |
671 » » » ports = append(ports, params.Port{ | 695 » » » ports = append(ports, instance.Port{ |
672 Protocol: p.Protocol, | 696 Protocol: p.Protocol, |
673 Number: i, | 697 Number: i, |
674 }) | 698 }) |
675 } | 699 } |
676 } | 700 } |
677 state.SortPorts(ports) | 701 state.SortPorts(ports) |
678 return ports, nil | 702 return ports, nil |
679 } | 703 } |
680 | 704 |
681 func (e *environ) OpenPorts(ports []params.Port) error { | 705 func (e *environ) OpenPorts(ports []instance.Port) error { |
682 if e.Config().FirewallMode() != config.FwGlobal { | 706 if e.Config().FirewallMode() != config.FwGlobal { |
683 return fmt.Errorf("invalid firewall mode for opening ports on en
vironment: %q", | 707 return fmt.Errorf("invalid firewall mode for opening ports on en
vironment: %q", |
684 e.Config().FirewallMode()) | 708 e.Config().FirewallMode()) |
685 } | 709 } |
686 if err := e.openPortsInGroup(e.globalGroupName(), ports); err != nil { | 710 if err := e.openPortsInGroup(e.globalGroupName(), ports); err != nil { |
687 return err | 711 return err |
688 } | 712 } |
689 log.Infof("environs/ec2: opened ports in global group: %v", ports) | 713 log.Infof("environs/ec2: opened ports in global group: %v", ports) |
690 return nil | 714 return nil |
691 } | 715 } |
692 | 716 |
693 func (e *environ) ClosePorts(ports []params.Port) error { | 717 func (e *environ) ClosePorts(ports []instance.Port) error { |
694 if e.Config().FirewallMode() != config.FwGlobal { | 718 if e.Config().FirewallMode() != config.FwGlobal { |
695 return fmt.Errorf("invalid firewall mode for closing ports on en
vironment: %q", | 719 return fmt.Errorf("invalid firewall mode for closing ports on en
vironment: %q", |
696 e.Config().FirewallMode()) | 720 e.Config().FirewallMode()) |
697 } | 721 } |
698 if err := e.closePortsInGroup(e.globalGroupName(), ports); err != nil { | 722 if err := e.closePortsInGroup(e.globalGroupName(), ports); err != nil { |
699 return err | 723 return err |
700 } | 724 } |
701 log.Infof("environs/ec2: closed ports in global group: %v", ports) | 725 log.Infof("environs/ec2: closed ports in global group: %v", ports) |
702 return nil | 726 return nil |
703 } | 727 } |
704 | 728 |
705 func (e *environ) Ports() ([]params.Port, error) { | 729 func (e *environ) Ports() ([]instance.Port, error) { |
706 if e.Config().FirewallMode() != config.FwGlobal { | 730 if e.Config().FirewallMode() != config.FwGlobal { |
707 return nil, fmt.Errorf("invalid firewall mode for retrieving por
ts from environment: %q", | 731 return nil, fmt.Errorf("invalid firewall mode for retrieving por
ts from environment: %q", |
708 e.Config().FirewallMode()) | 732 e.Config().FirewallMode()) |
709 } | 733 } |
710 return e.portsInGroup(e.globalGroupName()) | 734 return e.portsInGroup(e.globalGroupName()) |
711 } | 735 } |
712 | 736 |
713 func (*environ) Provider() environs.EnvironProvider { | 737 func (*environ) Provider() environs.EnvironProvider { |
714 return &providerInstance | 738 return &providerInstance |
715 } | 739 } |
716 | 740 |
717 func (e *environ) terminateInstances(ids []state.InstanceId) error { | 741 func (e *environ) terminateInstances(ids []instance.Id) error { |
718 if len(ids) == 0 { | 742 if len(ids) == 0 { |
719 return nil | 743 return nil |
720 } | 744 } |
721 var err error | 745 var err error |
722 ec2inst := e.ec2() | 746 ec2inst := e.ec2() |
723 strs := make([]string, len(ids)) | 747 strs := make([]string, len(ids)) |
724 for i, id := range ids { | 748 for i, id := range ids { |
725 strs[i] = string(id) | 749 strs[i] = string(id) |
726 } | 750 } |
727 for a := shortAttempt.Start(); a.Next(); { | 751 for a := shortAttempt.Start(); a.Next(); { |
(...skipping 26 matching lines...) Expand all Loading... |
754 } | 778 } |
755 | 779 |
756 func (e *environ) machineGroupName(machineId string) string { | 780 func (e *environ) machineGroupName(machineId string) string { |
757 return fmt.Sprintf("%s-%s", e.jujuGroupName(), machineId) | 781 return fmt.Sprintf("%s-%s", e.jujuGroupName(), machineId) |
758 } | 782 } |
759 | 783 |
760 func (e *environ) jujuGroupName() string { | 784 func (e *environ) jujuGroupName() string { |
761 return "juju-" + e.name | 785 return "juju-" + e.name |
762 } | 786 } |
763 | 787 |
764 func (inst *instance) OpenPorts(machineId string, ports []params.Port) error { | 788 func (inst *ec2Instance) OpenPorts(machineId string, ports []instance.Port) erro
r { |
765 if inst.e.Config().FirewallMode() != config.FwInstance { | 789 if inst.e.Config().FirewallMode() != config.FwInstance { |
766 return fmt.Errorf("invalid firewall mode for opening ports on in
stance: %q", | 790 return fmt.Errorf("invalid firewall mode for opening ports on in
stance: %q", |
767 inst.e.Config().FirewallMode()) | 791 inst.e.Config().FirewallMode()) |
768 } | 792 } |
769 name := inst.e.machineGroupName(machineId) | 793 name := inst.e.machineGroupName(machineId) |
770 if err := inst.e.openPortsInGroup(name, ports); err != nil { | 794 if err := inst.e.openPortsInGroup(name, ports); err != nil { |
771 return err | 795 return err |
772 } | 796 } |
773 log.Infof("environs/ec2: opened ports in security group %s: %v", name, p
orts) | 797 log.Infof("environs/ec2: opened ports in security group %s: %v", name, p
orts) |
774 return nil | 798 return nil |
775 } | 799 } |
776 | 800 |
777 func (inst *instance) ClosePorts(machineId string, ports []params.Port) error { | 801 func (inst *ec2Instance) ClosePorts(machineId string, ports []instance.Port) err
or { |
778 if inst.e.Config().FirewallMode() != config.FwInstance { | 802 if inst.e.Config().FirewallMode() != config.FwInstance { |
779 return fmt.Errorf("invalid firewall mode for closing ports on in
stance: %q", | 803 return fmt.Errorf("invalid firewall mode for closing ports on in
stance: %q", |
780 inst.e.Config().FirewallMode()) | 804 inst.e.Config().FirewallMode()) |
781 } | 805 } |
782 name := inst.e.machineGroupName(machineId) | 806 name := inst.e.machineGroupName(machineId) |
783 if err := inst.e.closePortsInGroup(name, ports); err != nil { | 807 if err := inst.e.closePortsInGroup(name, ports); err != nil { |
784 return err | 808 return err |
785 } | 809 } |
786 log.Infof("environs/ec2: closed ports in security group %s: %v", name, p
orts) | 810 log.Infof("environs/ec2: closed ports in security group %s: %v", name, p
orts) |
787 return nil | 811 return nil |
788 } | 812 } |
789 | 813 |
790 func (inst *instance) Ports(machineId string) ([]params.Port, error) { | 814 func (inst *ec2Instance) Ports(machineId string) ([]instance.Port, error) { |
791 if inst.e.Config().FirewallMode() != config.FwInstance { | 815 if inst.e.Config().FirewallMode() != config.FwInstance { |
792 return nil, fmt.Errorf("invalid firewall mode for retrieving por
ts from instance: %q", | 816 return nil, fmt.Errorf("invalid firewall mode for retrieving por
ts from instance: %q", |
793 inst.e.Config().FirewallMode()) | 817 inst.e.Config().FirewallMode()) |
794 } | 818 } |
795 name := inst.e.machineGroupName(machineId) | 819 name := inst.e.machineGroupName(machineId) |
796 return inst.e.portsInGroup(name) | 820 return inst.e.portsInGroup(name) |
797 } | 821 } |
798 | 822 |
799 // setUpGroups creates the security groups for the new machine, and | 823 // setUpGroups creates the security groups for the new machine, and |
800 // returns them. | 824 // returns them. |
(...skipping 208 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1009 } | 1033 } |
1010 var data []byte | 1034 var data []byte |
1011 data, err = ioutil.ReadAll(resp.Body) | 1035 data, err = ioutil.ReadAll(resp.Body) |
1012 if err != nil { | 1036 if err != nil { |
1013 continue | 1037 continue |
1014 } | 1038 } |
1015 return strings.TrimSpace(string(data)), nil | 1039 return strings.TrimSpace(string(data)), nil |
1016 } | 1040 } |
1017 return | 1041 return |
1018 } | 1042 } |
LEFT | RIGHT |