github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/provider/ec2/environ.go (about) 1 // Copyright 2011-2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package ec2 5 6 import ( 7 "fmt" 8 "math/rand" 9 "net" 10 "strings" 11 "sync" 12 "time" 13 14 "github.com/juju/errors" 15 "github.com/juju/retry" 16 "github.com/juju/utils" 17 "github.com/juju/utils/clock" 18 "gopkg.in/amz.v3/aws" 19 "gopkg.in/amz.v3/ec2" 20 "gopkg.in/juju/names.v2" 21 22 "github.com/juju/juju/cloudconfig/instancecfg" 23 "github.com/juju/juju/cloudconfig/providerinit" 24 "github.com/juju/juju/constraints" 25 "github.com/juju/juju/environs" 26 "github.com/juju/juju/environs/config" 27 "github.com/juju/juju/environs/instances" 28 "github.com/juju/juju/environs/simplestreams" 29 "github.com/juju/juju/environs/tags" 30 "github.com/juju/juju/instance" 31 "github.com/juju/juju/network" 32 "github.com/juju/juju/provider/common" 33 "github.com/juju/juju/provider/ec2/internal/ec2instancetypes" 34 "github.com/juju/juju/tools" 35 ) 36 37 const ( 38 invalidParameterValue = "InvalidParameterValue" 39 40 // tagName is the AWS-specific tag key that populates resources' 41 // name columns in the console. 42 tagName = "Name" 43 ) 44 45 var ( 46 // Use shortAttempt to poll for short-term events or for retrying API calls. 47 // TODO(katco): 2016-08-09: lp:1611427 48 shortAttempt = utils.AttemptStrategy{ 49 Total: 5 * time.Second, 50 Delay: 200 * time.Millisecond, 51 } 52 53 // aliveInstanceStates are the states which we filter by when listing 54 // instances in an environment. 55 aliveInstanceStates = []string{"pending", "running"} 56 ) 57 58 type environ struct { 59 name string 60 cloud environs.CloudSpec 61 ec2 *ec2.EC2 62 63 // ecfgMutex protects the *Unlocked fields below. 64 ecfgMutex sync.Mutex 65 ecfgUnlocked *environConfig 66 67 availabilityZonesMutex sync.Mutex 68 availabilityZones []common.AvailabilityZone 69 70 defaultVPCMutex sync.Mutex 71 defaultVPCChecked bool 72 defaultVPC *ec2.VPC 73 } 74 75 func (e *environ) Config() *config.Config { 76 return e.ecfg().Config 77 } 78 79 func (e *environ) SetConfig(cfg *config.Config) error { 80 ecfg, err := providerInstance.newConfig(cfg) 81 if err != nil { 82 return errors.Trace(err) 83 } 84 e.ecfgMutex.Lock() 85 e.ecfgUnlocked = ecfg 86 e.ecfgMutex.Unlock() 87 return nil 88 } 89 90 func (e *environ) ecfg() *environConfig { 91 e.ecfgMutex.Lock() 92 ecfg := e.ecfgUnlocked 93 e.ecfgMutex.Unlock() 94 return ecfg 95 } 96 97 func (e *environ) Name() string { 98 return e.name 99 } 100 101 // PrepareForBootstrap is part of the Environ interface. 102 func (env *environ) PrepareForBootstrap(ctx environs.BootstrapContext) error { 103 if ctx.ShouldVerifyCredentials() { 104 if err := verifyCredentials(env); err != nil { 105 return err 106 } 107 } 108 ecfg := env.ecfg() 109 vpcID, forceVPCID := ecfg.vpcID(), ecfg.forceVPCID() 110 if err := validateBootstrapVPC(env.ec2, env.cloud.Region, vpcID, forceVPCID, ctx); err != nil { 111 return errors.Trace(err) 112 } 113 return nil 114 } 115 116 // Create is part of the Environ interface. 117 func (env *environ) Create(args environs.CreateParams) error { 118 if err := verifyCredentials(env); err != nil { 119 return err 120 } 121 vpcID := env.ecfg().vpcID() 122 if err := validateModelVPC(env.ec2, env.name, vpcID); err != nil { 123 return errors.Trace(err) 124 } 125 // TODO(axw) 2016-08-04 #1609643 126 // Create global security group(s) here. 127 return nil 128 } 129 130 // Bootstrap is part of the Environ interface. 131 func (e *environ) Bootstrap(ctx environs.BootstrapContext, args environs.BootstrapParams) (*environs.BootstrapResult, error) { 132 return common.Bootstrap(ctx, e, args) 133 } 134 135 // BootstrapMessage is part of the Environ interface. 136 func (e *environ) BootstrapMessage() string { 137 return "" 138 } 139 140 // SupportsSpaces is specified on environs.Networking. 141 func (e *environ) SupportsSpaces() (bool, error) { 142 return true, nil 143 } 144 145 // SupportsSpaceDiscovery is specified on environs.Networking. 146 func (e *environ) SupportsSpaceDiscovery() (bool, error) { 147 return false, nil 148 } 149 150 var unsupportedConstraints = []string{ 151 constraints.Tags, 152 // TODO(anastasiamac 2016-03-16) LP#1557874 153 // use virt-type in StartInstances 154 constraints.VirtType, 155 } 156 157 // ConstraintsValidator is defined on the Environs interface. 158 func (e *environ) ConstraintsValidator() (constraints.Validator, error) { 159 validator := constraints.NewValidator() 160 validator.RegisterConflicts( 161 []string{constraints.InstanceType}, 162 []string{constraints.Mem, constraints.Cores, constraints.CpuPower}) 163 validator.RegisterUnsupported(unsupportedConstraints) 164 instanceTypes, err := e.supportedInstanceTypes() 165 if err != nil { 166 return nil, errors.Trace(err) 167 } 168 instTypeNames := make([]string, len(instanceTypes)) 169 for i, itype := range instanceTypes { 170 instTypeNames[i] = itype.Name 171 } 172 validator.RegisterVocabulary(constraints.InstanceType, instTypeNames) 173 return validator, nil 174 } 175 176 func archMatches(arches []string, arch *string) bool { 177 if arch == nil { 178 return true 179 } 180 for _, a := range arches { 181 if a == *arch { 182 return true 183 } 184 } 185 return false 186 } 187 188 var ec2AvailabilityZones = (*ec2.EC2).AvailabilityZones 189 190 type ec2AvailabilityZone struct { 191 ec2.AvailabilityZoneInfo 192 } 193 194 func (z *ec2AvailabilityZone) Name() string { 195 return z.AvailabilityZoneInfo.Name 196 } 197 198 func (z *ec2AvailabilityZone) Available() bool { 199 return z.AvailabilityZoneInfo.State == availableState 200 } 201 202 // AvailabilityZones returns a slice of availability zones 203 // for the configured region. 204 func (e *environ) AvailabilityZones() ([]common.AvailabilityZone, error) { 205 e.availabilityZonesMutex.Lock() 206 defer e.availabilityZonesMutex.Unlock() 207 if e.availabilityZones == nil { 208 filter := ec2.NewFilter() 209 filter.Add("region-name", e.cloud.Region) 210 resp, err := ec2AvailabilityZones(e.ec2, filter) 211 if err != nil { 212 return nil, err 213 } 214 logger.Debugf("availability zones: %+v", resp) 215 e.availabilityZones = make([]common.AvailabilityZone, len(resp.Zones)) 216 for i, z := range resp.Zones { 217 e.availabilityZones[i] = &ec2AvailabilityZone{z} 218 } 219 } 220 return e.availabilityZones, nil 221 } 222 223 // InstanceAvailabilityZoneNames returns the availability zone names for each 224 // of the specified instances. 225 func (e *environ) InstanceAvailabilityZoneNames(ids []instance.Id) ([]string, error) { 226 instances, err := e.Instances(ids) 227 if err != nil && err != environs.ErrPartialInstances { 228 return nil, err 229 } 230 zones := make([]string, len(instances)) 231 for i, inst := range instances { 232 if inst == nil { 233 continue 234 } 235 zones[i] = inst.(*ec2Instance).AvailZone 236 } 237 return zones, err 238 } 239 240 type ec2Placement struct { 241 availabilityZone ec2.AvailabilityZoneInfo 242 } 243 244 func (e *environ) parsePlacement(placement string) (*ec2Placement, error) { 245 pos := strings.IndexRune(placement, '=') 246 if pos == -1 { 247 return nil, fmt.Errorf("unknown placement directive: %v", placement) 248 } 249 switch key, value := placement[:pos], placement[pos+1:]; key { 250 case "zone": 251 availabilityZone := value 252 zones, err := e.AvailabilityZones() 253 if err != nil { 254 return nil, err 255 } 256 for _, z := range zones { 257 if z.Name() == availabilityZone { 258 return &ec2Placement{ 259 z.(*ec2AvailabilityZone).AvailabilityZoneInfo, 260 }, nil 261 } 262 } 263 return nil, fmt.Errorf("invalid availability zone %q", availabilityZone) 264 } 265 return nil, fmt.Errorf("unknown placement directive: %v", placement) 266 } 267 268 // PrecheckInstance is defined on the state.Prechecker interface. 269 func (e *environ) PrecheckInstance(series string, cons constraints.Value, placement string) error { 270 if placement != "" { 271 if _, err := e.parsePlacement(placement); err != nil { 272 return err 273 } 274 } 275 if !cons.HasInstanceType() { 276 return nil 277 } 278 // Constraint has an instance-type constraint so let's see if it is valid. 279 instanceTypes, err := e.supportedInstanceTypes() 280 if err != nil { 281 return errors.Trace(err) 282 } 283 for _, itype := range instanceTypes { 284 if itype.Name != *cons.InstanceType { 285 continue 286 } 287 if archMatches(itype.Arches, cons.Arch) { 288 return nil 289 } 290 } 291 if cons.Arch == nil { 292 return fmt.Errorf("invalid AWS instance type %q specified", *cons.InstanceType) 293 } 294 return fmt.Errorf("invalid AWS instance type %q and arch %q specified", *cons.InstanceType, *cons.Arch) 295 } 296 297 // MetadataLookupParams returns parameters which are used to query simplestreams metadata. 298 func (e *environ) MetadataLookupParams(region string) (*simplestreams.MetadataLookupParams, error) { 299 var endpoint string 300 if region == "" { 301 region = e.cloud.Region 302 endpoint = e.cloud.Endpoint 303 } else { 304 // TODO(axw) 2016-10-04 #1630089 305 // MetadataLookupParams needs to be updated so that providers 306 // are not expected to know how to map regions to endpoints. 307 ec2Region, ok := aws.Regions[region] 308 if !ok { 309 return nil, errors.Errorf("unknown region %q", region) 310 } 311 endpoint = ec2Region.EC2Endpoint 312 } 313 return &simplestreams.MetadataLookupParams{ 314 Series: config.PreferredSeries(e.ecfg()), 315 Region: region, 316 Endpoint: endpoint, 317 }, nil 318 } 319 320 // Region is specified in the HasRegion interface. 321 func (e *environ) Region() (simplestreams.CloudSpec, error) { 322 return simplestreams.CloudSpec{ 323 Region: e.cloud.Region, 324 Endpoint: e.cloud.Endpoint, 325 }, nil 326 } 327 328 const ( 329 ebsStorage = "ebs" 330 ssdStorage = "ssd" 331 ) 332 333 // DistributeInstances implements the state.InstanceDistributor policy. 334 func (e *environ) DistributeInstances(candidates, distributionGroup []instance.Id) ([]instance.Id, error) { 335 return common.DistributeInstances(e, candidates, distributionGroup) 336 } 337 338 var availabilityZoneAllocations = common.AvailabilityZoneAllocations 339 340 // MaintainInstance is specified in the InstanceBroker interface. 341 func (*environ) MaintainInstance(args environs.StartInstanceParams) error { 342 return nil 343 } 344 345 // resourceName returns the string to use for a resource's Name tag, 346 // to help users identify Juju-managed resources in the AWS console. 347 func resourceName(tag names.Tag, envName string) string { 348 return fmt.Sprintf("juju-%s-%s", envName, tag) 349 } 350 351 // StartInstance is specified in the InstanceBroker interface. 352 func (e *environ) StartInstance(args environs.StartInstanceParams) (_ *environs.StartInstanceResult, resultErr error) { 353 if args.ControllerUUID == "" { 354 return nil, errors.New("missing controller UUID") 355 } 356 var inst *ec2Instance 357 defer func() { 358 if resultErr == nil || inst == nil { 359 return 360 } 361 if err := e.StopInstances(inst.Id()); err != nil { 362 logger.Errorf("error stopping failed instance: %v", err) 363 } 364 }() 365 366 var availabilityZones []string 367 if args.Placement != "" { 368 placement, err := e.parsePlacement(args.Placement) 369 if err != nil { 370 return nil, err 371 } 372 if placement.availabilityZone.State != availableState { 373 return nil, errors.Errorf("availability zone %q is %s", placement.availabilityZone.Name, placement.availabilityZone.State) 374 } 375 availabilityZones = append(availabilityZones, placement.availabilityZone.Name) 376 } 377 378 // If no availability zone is specified, then automatically spread across 379 // the known zones for optimal spread across the instance distribution 380 // group. 381 var zoneInstances []common.AvailabilityZoneInstances 382 if len(availabilityZones) == 0 { 383 var err error 384 var group []instance.Id 385 if args.DistributionGroup != nil { 386 group, err = args.DistributionGroup() 387 if err != nil { 388 return nil, err 389 } 390 } 391 zoneInstances, err = availabilityZoneAllocations(e, group) 392 if err != nil { 393 return nil, err 394 } 395 for _, z := range zoneInstances { 396 availabilityZones = append(availabilityZones, z.ZoneName) 397 } 398 if len(availabilityZones) == 0 { 399 return nil, errors.New("failed to determine availability zones") 400 } 401 } 402 403 arches := args.Tools.Arches() 404 405 instanceTypes, err := e.supportedInstanceTypes() 406 if err != nil { 407 return nil, errors.Trace(err) 408 } 409 410 spec, err := findInstanceSpec( 411 args.InstanceConfig.Controller != nil, 412 args.ImageMetadata, 413 instanceTypes, 414 &instances.InstanceConstraint{ 415 Region: e.cloud.Region, 416 Series: args.InstanceConfig.Series, 417 Arches: arches, 418 Constraints: args.Constraints, 419 Storage: []string{ssdStorage, ebsStorage}, 420 }, 421 ) 422 if err != nil { 423 return nil, err 424 } 425 tools, err := args.Tools.Match(tools.Filter{Arch: spec.Image.Arch}) 426 if err != nil { 427 return nil, errors.Errorf("chosen architecture %v not present in %v", spec.Image.Arch, arches) 428 } 429 430 if spec.InstanceType.Deprecated { 431 logger.Infof("deprecated instance type specified: %s", spec.InstanceType.Name) 432 } 433 434 if err := args.InstanceConfig.SetTools(tools); err != nil { 435 return nil, errors.Trace(err) 436 } 437 if err := instancecfg.FinishInstanceConfig(args.InstanceConfig, e.Config()); err != nil { 438 return nil, err 439 } 440 441 userData, err := providerinit.ComposeUserData(args.InstanceConfig, nil, AmazonRenderer{}) 442 if err != nil { 443 return nil, errors.Annotate(err, "cannot make user data") 444 } 445 logger.Debugf("ec2 user data; %d bytes", len(userData)) 446 var apiPort int 447 if args.InstanceConfig.Controller != nil { 448 apiPort = args.InstanceConfig.Controller.Config.APIPort() 449 } else { 450 apiPort = args.InstanceConfig.APIInfo.Ports()[0] 451 } 452 groups, err := e.setUpGroups(args.ControllerUUID, args.InstanceConfig.MachineId, apiPort) 453 if err != nil { 454 return nil, errors.Annotate(err, "cannot set up groups") 455 } 456 457 blockDeviceMappings := getBlockDeviceMappings( 458 args.Constraints, 459 args.InstanceConfig.Series, 460 args.InstanceConfig.Controller != nil, 461 ) 462 rootDiskSize := uint64(blockDeviceMappings[0].VolumeSize) * 1024 463 464 // If --constraints spaces=foo was passed, the provisioner will populate 465 // args.SubnetsToZones map. In AWS a subnet can span only one zone, so here 466 // we build the reverse map zonesToSubnets, which we will use to below in 467 // the RunInstance loop to provide an explicit subnet ID, rather than just 468 // AZ. This ensures instances in the same group (units of a service or all 469 // instances when adding a machine manually) will still be evenly 470 // distributed across AZs, but only within subnets of the space constraint. 471 // 472 // TODO(dimitern): This should be done in a provider-independant way. 473 if spaces := args.Constraints.IncludeSpaces(); len(spaces) > 1 { 474 logger.Infof("ignoring all but the first positive space from constraints: %v", spaces) 475 } 476 477 var instResp *ec2.RunInstancesResp 478 commonRunArgs := &ec2.RunInstances{ 479 MinCount: 1, 480 MaxCount: 1, 481 UserData: userData, 482 InstanceType: spec.InstanceType.Name, 483 SecurityGroups: groups, 484 BlockDeviceMappings: blockDeviceMappings, 485 ImageId: spec.Image.Id, 486 } 487 488 haveVPCID := isVPCIDSet(e.ecfg().vpcID()) 489 490 for _, zone := range availabilityZones { 491 runArgs := commonRunArgs 492 runArgs.AvailZone = zone 493 494 var subnetIDsForZone []string 495 var subnetErr error 496 if haveVPCID { 497 var allowedSubnetIDs []string 498 for subnetID, _ := range args.SubnetsToZones { 499 allowedSubnetIDs = append(allowedSubnetIDs, string(subnetID)) 500 } 501 subnetIDsForZone, subnetErr = getVPCSubnetIDsForAvailabilityZone(e.ec2, e.ecfg().vpcID(), zone, allowedSubnetIDs) 502 } else if args.Constraints.HaveSpaces() { 503 subnetIDsForZone, subnetErr = findSubnetIDsForAvailabilityZone(zone, args.SubnetsToZones) 504 } 505 506 switch { 507 case subnetErr != nil && errors.IsNotFound(subnetErr): 508 logger.Infof("no matching subnets in zone %q; assuming zone is constrained and trying another", zone) 509 continue 510 case subnetErr != nil: 511 return nil, errors.Annotatef(subnetErr, "getting subnets for zone %q", zone) 512 case len(subnetIDsForZone) > 1: 513 // With multiple equally suitable subnets, picking one at random 514 // will allow for better instance spread within the same zone, and 515 // still work correctly if we happen to pick a constrained subnet 516 // (we'll just treat this the same way we treat constrained zones 517 // and retry). 518 runArgs.SubnetId = subnetIDsForZone[rand.Intn(len(subnetIDsForZone))] 519 logger.Infof( 520 "selected random subnet %q from all matching in zone %q: %v", 521 runArgs.SubnetId, zone, subnetIDsForZone, 522 ) 523 case len(subnetIDsForZone) == 1: 524 runArgs.SubnetId = subnetIDsForZone[0] 525 logger.Infof("selected subnet %q in zone %q", runArgs.SubnetId, zone) 526 } 527 528 instResp, err = runInstances(e.ec2, runArgs) 529 if err == nil || !isZoneOrSubnetConstrainedError(err) { 530 break 531 } 532 533 logger.Infof("%q is constrained, trying another availability zone", zone) 534 } 535 536 if err != nil { 537 return nil, errors.Annotate(err, "cannot run instances") 538 } 539 if len(instResp.Instances) != 1 { 540 return nil, errors.Errorf("expected 1 started instance, got %d", len(instResp.Instances)) 541 } 542 543 inst = &ec2Instance{ 544 e: e, 545 Instance: &instResp.Instances[0], 546 } 547 instAZ := inst.Instance.AvailZone 548 if haveVPCID { 549 instVPC := e.ecfg().vpcID() 550 instSubnet := inst.Instance.SubnetId 551 logger.Infof("started instance %q in AZ %q, subnet %q, VPC %q", inst.Id(), instAZ, instSubnet, instVPC) 552 } else { 553 logger.Infof("started instance %q in AZ %q", inst.Id(), instAZ) 554 } 555 556 // Tag instance, for accounting and identification. 557 instanceName := resourceName( 558 names.NewMachineTag(args.InstanceConfig.MachineId), e.Config().Name(), 559 ) 560 args.InstanceConfig.Tags[tagName] = instanceName 561 if err := tagResources(e.ec2, args.InstanceConfig.Tags, string(inst.Id())); err != nil { 562 return nil, errors.Annotate(err, "tagging instance") 563 } 564 565 // Tag the machine's root EBS volume, if it has one. 566 if inst.Instance.RootDeviceType == "ebs" { 567 cfg := e.Config() 568 tags := tags.ResourceTags( 569 names.NewModelTag(cfg.UUID()), 570 names.NewControllerTag(args.ControllerUUID), 571 cfg, 572 ) 573 tags[tagName] = instanceName + "-root" 574 if err := tagRootDisk(e.ec2, tags, inst.Instance); err != nil { 575 return nil, errors.Annotate(err, "tagging root disk") 576 } 577 } 578 579 hc := instance.HardwareCharacteristics{ 580 Arch: &spec.Image.Arch, 581 Mem: &spec.InstanceType.Mem, 582 CpuCores: &spec.InstanceType.CpuCores, 583 CpuPower: spec.InstanceType.CpuPower, 584 RootDisk: &rootDiskSize, 585 // Tags currently not supported by EC2 586 AvailabilityZone: &inst.Instance.AvailZone, 587 } 588 return &environs.StartInstanceResult{ 589 Instance: inst, 590 Hardware: &hc, 591 }, nil 592 } 593 594 // tagResources calls ec2.CreateTags, tagging each of the specified resources 595 // with the given tags. tagResources will retry for a short period of time 596 // if it receives a *.NotFound error response from EC2. 597 func tagResources(e *ec2.EC2, tags map[string]string, resourceIds ...string) error { 598 if len(tags) == 0 { 599 return nil 600 } 601 ec2Tags := make([]ec2.Tag, 0, len(tags)) 602 for k, v := range tags { 603 ec2Tags = append(ec2Tags, ec2.Tag{k, v}) 604 } 605 var err error 606 for a := shortAttempt.Start(); a.Next(); { 607 _, err = e.CreateTags(resourceIds, ec2Tags) 608 if err == nil || !strings.HasSuffix(ec2ErrCode(err), ".NotFound") { 609 return err 610 } 611 } 612 return err 613 } 614 615 func tagRootDisk(e *ec2.EC2, tags map[string]string, inst *ec2.Instance) error { 616 if len(tags) == 0 { 617 return nil 618 } 619 findVolumeId := func(inst *ec2.Instance) string { 620 for _, m := range inst.BlockDeviceMappings { 621 if m.DeviceName != inst.RootDeviceName { 622 continue 623 } 624 return m.VolumeId 625 } 626 return "" 627 } 628 // Wait until the instance has an associated EBS volume in the 629 // block-device-mapping. 630 volumeId := findVolumeId(inst) 631 // TODO(katco): 2016-08-09: lp:1611427 632 waitRootDiskAttempt := utils.AttemptStrategy{ 633 Total: 5 * time.Minute, 634 Delay: 5 * time.Second, 635 } 636 for a := waitRootDiskAttempt.Start(); volumeId == "" && a.Next(); { 637 resp, err := e.Instances([]string{inst.InstanceId}, nil) 638 if err = errors.Annotate(err, "cannot fetch instance information"); err != nil { 639 logger.Warningf("%v", err) 640 if a.HasNext() == false { 641 return err 642 } 643 logger.Infof("retrying fetch of instances") 644 continue 645 } 646 if len(resp.Reservations) > 0 && len(resp.Reservations[0].Instances) > 0 { 647 inst = &resp.Reservations[0].Instances[0] 648 volumeId = findVolumeId(inst) 649 } 650 } 651 if volumeId == "" { 652 return errors.New("timed out waiting for EBS volume to be associated") 653 } 654 return tagResources(e, tags, volumeId) 655 } 656 657 var runInstances = _runInstances 658 659 // runInstances calls ec2.RunInstances for a fixed number of attempts until 660 // RunInstances returns an error code that does not indicate an error that 661 // may be caused by eventual consistency. 662 func _runInstances(e *ec2.EC2, ri *ec2.RunInstances) (resp *ec2.RunInstancesResp, err error) { 663 for a := shortAttempt.Start(); a.Next(); { 664 resp, err = e.RunInstances(ri) 665 if err == nil || !isNotFoundError(err) { 666 break 667 } 668 } 669 return resp, err 670 } 671 672 func (e *environ) StopInstances(ids ...instance.Id) error { 673 return errors.Trace(e.terminateInstances(ids)) 674 } 675 676 // groupInfoByName returns information on the security group 677 // with the given name including rules and other details. 678 func (e *environ) groupInfoByName(groupName string) (ec2.SecurityGroupInfo, error) { 679 resp, err := e.securityGroupsByNameOrID(groupName) 680 if err != nil { 681 return ec2.SecurityGroupInfo{}, err 682 } 683 684 if len(resp.Groups) != 1 { 685 return ec2.SecurityGroupInfo{}, errors.NewNotFound(fmt.Errorf( 686 "expected one security group named %q, got %v", 687 groupName, resp.Groups, 688 ), "") 689 } 690 return resp.Groups[0], nil 691 } 692 693 // groupByName returns the security group with the given name. 694 func (e *environ) groupByName(groupName string) (ec2.SecurityGroup, error) { 695 groupInfo, err := e.groupInfoByName(groupName) 696 return groupInfo.SecurityGroup, err 697 } 698 699 // isNotFoundError returns whether err is a typed NotFoundError or an EC2 error 700 // code for "group not found", indicating no matching instances (as they are 701 // filtered by group). 702 func isNotFoundError(err error) bool { 703 return err != nil && (errors.IsNotFound(err) || ec2ErrCode(err) == "InvalidGroup.NotFound") 704 } 705 706 // Instances is part of the environs.Environ interface. 707 func (e *environ) Instances(ids []instance.Id) ([]instance.Instance, error) { 708 if len(ids) == 0 { 709 return nil, nil 710 } 711 insts := make([]instance.Instance, len(ids)) 712 // Make a series of requests to cope with eventual consistency. 713 // Each request will attempt to add more instances to the requested 714 // set. 715 var err error 716 for a := shortAttempt.Start(); a.Next(); { 717 var need []string 718 for i, inst := range insts { 719 if inst == nil { 720 need = append(need, string(ids[i])) 721 } 722 } 723 filter := ec2.NewFilter() 724 filter.Add("instance-state-name", aliveInstanceStates...) 725 filter.Add("instance-id", need...) 726 e.addModelFilter(filter) 727 err = e.gatherInstances(ids, insts, filter) 728 if err == nil || err != environs.ErrPartialInstances { 729 break 730 } 731 } 732 if err == environs.ErrPartialInstances { 733 for _, inst := range insts { 734 if inst != nil { 735 return insts, environs.ErrPartialInstances 736 } 737 } 738 return nil, environs.ErrNoInstances 739 } 740 if err != nil { 741 return nil, err 742 } 743 return insts, nil 744 } 745 746 // gatherInstances tries to get information on each instance 747 // id whose corresponding insts slot is nil. 748 // 749 // This function returns environs.ErrPartialInstances if the 750 // insts slice has not been completely filled. 751 func (e *environ) gatherInstances( 752 ids []instance.Id, 753 insts []instance.Instance, 754 filter *ec2.Filter, 755 ) error { 756 resp, err := e.ec2.Instances(nil, filter) 757 if err != nil { 758 return err 759 } 760 n := 0 761 // For each requested id, add it to the returned instances 762 // if we find it in the response. 763 for i, id := range ids { 764 if insts[i] != nil { 765 n++ 766 continue 767 } 768 for j := range resp.Reservations { 769 r := &resp.Reservations[j] 770 for k := range r.Instances { 771 if r.Instances[k].InstanceId != string(id) { 772 continue 773 } 774 inst := r.Instances[k] 775 // TODO(wallyworld): lookup the details to fill in the instance type data 776 insts[i] = &ec2Instance{e: e, Instance: &inst} 777 n++ 778 } 779 } 780 } 781 if n < len(ids) { 782 return environs.ErrPartialInstances 783 } 784 return nil 785 } 786 787 // NetworkInterfaces implements NetworkingEnviron.NetworkInterfaces. 788 func (e *environ) NetworkInterfaces(instId instance.Id) ([]network.InterfaceInfo, error) { 789 var err error 790 var networkInterfacesResp *ec2.NetworkInterfacesResp 791 for a := shortAttempt.Start(); a.Next(); { 792 logger.Tracef("retrieving NICs for instance %q", instId) 793 filter := ec2.NewFilter() 794 filter.Add("attachment.instance-id", string(instId)) 795 networkInterfacesResp, err = e.ec2.NetworkInterfaces(nil, filter) 796 logger.Tracef("instance %q NICs: %#v (err: %v)", instId, networkInterfacesResp, err) 797 if err != nil { 798 logger.Errorf("failed to get instance %q interfaces: %v (retrying)", instId, err) 799 continue 800 } 801 if len(networkInterfacesResp.Interfaces) == 0 { 802 logger.Tracef("instance %q has no NIC attachment yet, retrying...", instId) 803 continue 804 } 805 logger.Tracef("found instance %q NICS: %#v", instId, networkInterfacesResp.Interfaces) 806 break 807 } 808 if err != nil { 809 // either the instance doesn't exist or we couldn't get through to 810 // the ec2 api 811 return nil, errors.Annotatef(err, "cannot get instance %q network interfaces", instId) 812 } 813 ec2Interfaces := networkInterfacesResp.Interfaces 814 result := make([]network.InterfaceInfo, len(ec2Interfaces)) 815 for i, iface := range ec2Interfaces { 816 resp, err := e.ec2.Subnets([]string{iface.SubnetId}, nil) 817 if err != nil { 818 return nil, errors.Annotatef(err, "failed to retrieve subnet %q info", iface.SubnetId) 819 } 820 if len(resp.Subnets) != 1 { 821 return nil, errors.Errorf("expected 1 subnet, got %d", len(resp.Subnets)) 822 } 823 subnet := resp.Subnets[0] 824 cidr := subnet.CIDRBlock 825 826 result[i] = network.InterfaceInfo{ 827 DeviceIndex: iface.Attachment.DeviceIndex, 828 MACAddress: iface.MACAddress, 829 CIDR: cidr, 830 ProviderId: network.Id(iface.Id), 831 ProviderSubnetId: network.Id(iface.SubnetId), 832 AvailabilityZones: []string{subnet.AvailZone}, 833 VLANTag: 0, // Not supported on EC2. 834 // Getting the interface name is not supported on EC2, so fake it. 835 InterfaceName: fmt.Sprintf("unsupported%d", iface.Attachment.DeviceIndex), 836 Disabled: false, 837 NoAutoStart: false, 838 ConfigType: network.ConfigDHCP, 839 InterfaceType: network.EthernetInterface, 840 Address: network.NewScopedAddress(iface.PrivateIPAddress, network.ScopeCloudLocal), 841 } 842 } 843 return result, nil 844 } 845 846 func makeSubnetInfo(cidr string, subnetId network.Id, availZones []string) (network.SubnetInfo, error) { 847 _, _, err := net.ParseCIDR(cidr) 848 if err != nil { 849 return network.SubnetInfo{}, errors.Annotatef(err, "skipping subnet %q, invalid CIDR", cidr) 850 } 851 852 info := network.SubnetInfo{ 853 CIDR: cidr, 854 ProviderId: subnetId, 855 VLANTag: 0, // Not supported on EC2 856 AvailabilityZones: availZones, 857 } 858 logger.Tracef("found subnet with info %#v", info) 859 return info, nil 860 861 } 862 863 // Spaces is not implemented by the ec2 provider as we don't currently have 864 // provider level spaces. 865 func (e *environ) Spaces() ([]network.SpaceInfo, error) { 866 return nil, errors.NotSupportedf("Spaces") 867 } 868 869 // Subnets returns basic information about the specified subnets known 870 // by the provider for the specified instance or list of ids. subnetIds can be 871 // empty, in which case all known are returned. Implements 872 // NetworkingEnviron.Subnets. 873 func (e *environ) Subnets(instId instance.Id, subnetIds []network.Id) ([]network.SubnetInfo, error) { 874 var results []network.SubnetInfo 875 subIdSet := make(map[string]bool) 876 for _, subId := range subnetIds { 877 subIdSet[string(subId)] = false 878 } 879 880 if instId != instance.UnknownId { 881 interfaces, err := e.NetworkInterfaces(instId) 882 if err != nil { 883 return results, errors.Trace(err) 884 } 885 if len(subnetIds) == 0 { 886 for _, iface := range interfaces { 887 subIdSet[string(iface.ProviderSubnetId)] = false 888 } 889 } 890 for _, iface := range interfaces { 891 _, ok := subIdSet[string(iface.ProviderSubnetId)] 892 if !ok { 893 logger.Tracef("subnet %q not in %v, skipping", iface.ProviderSubnetId, subnetIds) 894 continue 895 } 896 subIdSet[string(iface.ProviderSubnetId)] = true 897 info, err := makeSubnetInfo(iface.CIDR, iface.ProviderSubnetId, iface.AvailabilityZones) 898 if err != nil { 899 // Error will already have been logged. 900 continue 901 } 902 results = append(results, info) 903 } 904 } else { 905 resp, err := e.ec2.Subnets(nil, nil) 906 if err != nil { 907 return nil, errors.Annotatef(err, "failed to retrieve subnets") 908 } 909 if len(subnetIds) == 0 { 910 for _, subnet := range resp.Subnets { 911 subIdSet[subnet.Id] = false 912 } 913 } 914 915 for _, subnet := range resp.Subnets { 916 _, ok := subIdSet[subnet.Id] 917 if !ok { 918 logger.Tracef("subnet %q not in %v, skipping", subnet.Id, subnetIds) 919 continue 920 } 921 subIdSet[subnet.Id] = true 922 cidr := subnet.CIDRBlock 923 info, err := makeSubnetInfo(cidr, network.Id(subnet.Id), []string{subnet.AvailZone}) 924 if err != nil { 925 // Error will already have been logged. 926 continue 927 } 928 results = append(results, info) 929 930 } 931 } 932 933 notFound := []string{} 934 for subId, found := range subIdSet { 935 if !found { 936 notFound = append(notFound, subId) 937 } 938 } 939 if len(notFound) != 0 { 940 return nil, errors.Errorf("failed to find the following subnet ids: %v", notFound) 941 } 942 943 return results, nil 944 } 945 946 // AllInstances is part of the environs.InstanceBroker interface. 947 func (e *environ) AllInstances() ([]instance.Instance, error) { 948 return e.AllInstancesByState("pending", "running") 949 } 950 951 // AllInstancesByState returns all instances in the environment 952 // with one of the specified instance states. 953 func (e *environ) AllInstancesByState(states ...string) ([]instance.Instance, error) { 954 // NOTE(axw) we use security group filtering here because instances 955 // start out untagged. If Juju were to abort after starting an instance, 956 // but before tagging it, it would be leaked. We only need to do this 957 // for AllInstances, as it is the result of AllInstances that is used 958 // in "harvesting" unknown instances by the provisioner. 959 // 960 // One possible alternative is to modify ec2.RunInstances to allow the 961 // caller to specify ClientToken, and then format it like 962 // <controller-uuid>:<model-uuid>:<machine-id> 963 // (with base64-encoding to keep the size under the 64-byte limit) 964 // 965 // It is possible to filter on "client-token", and specify wildcards; 966 // therefore we could use client-token filters everywhere in the ec2 967 // provider instead of tags or security groups. The only danger is if 968 // we need to make non-idempotent calls to RunInstances for the machine 969 // ID. I don't think this is needed, but I am not confident enough to 970 // change this fundamental right now. 971 // 972 // An EC2 API call is required to resolve the group name to an id, as 973 // VPC enabled accounts do not support name based filtering. 974 groupName := e.jujuGroupName() 975 group, err := e.groupByName(groupName) 976 if isNotFoundError(err) { 977 // If there's no group, then there cannot be any instances. 978 return nil, nil 979 } else if err != nil { 980 return nil, errors.Trace(err) 981 } 982 filter := ec2.NewFilter() 983 filter.Add("instance-state-name", states...) 984 filter.Add("instance.group-id", group.Id) 985 return e.allInstances(filter) 986 } 987 988 // ControllerInstances is part of the environs.Environ interface. 989 func (e *environ) ControllerInstances(controllerUUID string) ([]instance.Id, error) { 990 filter := ec2.NewFilter() 991 filter.Add("instance-state-name", aliveInstanceStates...) 992 filter.Add(fmt.Sprintf("tag:%s", tags.JujuIsController), "true") 993 e.addControllerFilter(filter, controllerUUID) 994 ids, err := e.allInstanceIDs(filter) 995 if err != nil { 996 return nil, errors.Trace(err) 997 } 998 if len(ids) == 0 { 999 return nil, environs.ErrNotBootstrapped 1000 } 1001 return ids, nil 1002 } 1003 1004 // allControllerManagedInstances returns the IDs of all instances managed by 1005 // this environment's controller. 1006 // 1007 // Note that this requires that all instances are tagged; we cannot filter on 1008 // security groups, as we do not know the names of the models. 1009 func (e *environ) allControllerManagedInstances(controllerUUID string) ([]instance.Id, error) { 1010 filter := ec2.NewFilter() 1011 filter.Add("instance-state-name", aliveInstanceStates...) 1012 e.addControllerFilter(filter, controllerUUID) 1013 return e.allInstanceIDs(filter) 1014 } 1015 1016 func (e *environ) allInstanceIDs(filter *ec2.Filter) ([]instance.Id, error) { 1017 insts, err := e.allInstances(filter) 1018 if err != nil { 1019 return nil, errors.Trace(err) 1020 } 1021 ids := make([]instance.Id, len(insts)) 1022 for i, inst := range insts { 1023 ids[i] = inst.Id() 1024 } 1025 return ids, nil 1026 } 1027 1028 func (e *environ) allInstances(filter *ec2.Filter) ([]instance.Instance, error) { 1029 resp, err := e.ec2.Instances(nil, filter) 1030 if err != nil { 1031 return nil, errors.Annotate(err, "listing instances") 1032 } 1033 var insts []instance.Instance 1034 for _, r := range resp.Reservations { 1035 for i := range r.Instances { 1036 inst := r.Instances[i] 1037 // TODO(wallyworld): lookup the details to fill in the instance type data 1038 insts = append(insts, &ec2Instance{e: e, Instance: &inst}) 1039 } 1040 } 1041 return insts, nil 1042 } 1043 1044 // Destroy is part of the environs.Environ interface. 1045 func (e *environ) Destroy() error { 1046 if err := common.Destroy(e); err != nil { 1047 return errors.Trace(err) 1048 } 1049 if err := e.cleanEnvironmentSecurityGroups(); err != nil { 1050 return errors.Annotate(err, "cannot delete environment security groups") 1051 } 1052 return nil 1053 } 1054 1055 // DestroyController implements the Environ interface. 1056 func (e *environ) DestroyController(controllerUUID string) error { 1057 // In case any hosted environment hasn't been cleaned up yet, 1058 // we also attempt to delete their resources when the controller 1059 // environment is destroyed. 1060 if err := e.destroyControllerManagedEnvirons(controllerUUID); err != nil { 1061 return errors.Annotate(err, "destroying managed environs") 1062 } 1063 return e.Destroy() 1064 } 1065 1066 // destroyControllerManagedEnvirons destroys all environments managed by this 1067 // environment's controller. 1068 func (e *environ) destroyControllerManagedEnvirons(controllerUUID string) error { 1069 1070 // Terminate all instances managed by the controller. 1071 instIds, err := e.allControllerManagedInstances(controllerUUID) 1072 if err != nil { 1073 return errors.Annotate(err, "listing instances") 1074 } 1075 if err := e.terminateInstances(instIds); err != nil { 1076 return errors.Annotate(err, "terminating instances") 1077 } 1078 1079 // Delete all volumes managed by the controller. 1080 volIds, err := e.allControllerManagedVolumes(controllerUUID) 1081 if err != nil { 1082 return errors.Annotate(err, "listing volumes") 1083 } 1084 errs := destroyVolumes(e.ec2, volIds) 1085 for i, err := range errs { 1086 if err == nil { 1087 continue 1088 } 1089 return errors.Annotatef(err, "destroying volume %q", volIds[i], err) 1090 } 1091 1092 // Delete security groups managed by the controller. 1093 groups, err := e.controllerSecurityGroups(controllerUUID) 1094 if err != nil { 1095 return errors.Trace(err) 1096 } 1097 for _, g := range groups { 1098 if err := deleteSecurityGroupInsistently(e.ec2, g, clock.WallClock); err != nil { 1099 return errors.Annotatef( 1100 err, "cannot delete security group %q (%q)", 1101 g.Name, g.Id, 1102 ) 1103 } 1104 } 1105 return nil 1106 } 1107 1108 func (e *environ) allControllerManagedVolumes(controllerUUID string) ([]string, error) { 1109 filter := ec2.NewFilter() 1110 e.addControllerFilter(filter, controllerUUID) 1111 return listVolumes(e.ec2, filter) 1112 } 1113 1114 func portsToIPPerms(ports []network.PortRange) []ec2.IPPerm { 1115 ipPerms := make([]ec2.IPPerm, len(ports)) 1116 for i, p := range ports { 1117 ipPerms[i] = ec2.IPPerm{ 1118 Protocol: p.Protocol, 1119 FromPort: p.FromPort, 1120 ToPort: p.ToPort, 1121 SourceIPs: []string{"0.0.0.0/0"}, 1122 } 1123 } 1124 return ipPerms 1125 } 1126 1127 func (e *environ) openPortsInGroup(name string, ports []network.PortRange) error { 1128 if len(ports) == 0 { 1129 return nil 1130 } 1131 // Give permissions for anyone to access the given ports. 1132 g, err := e.groupByName(name) 1133 if err != nil { 1134 return err 1135 } 1136 ipPerms := portsToIPPerms(ports) 1137 _, err = e.ec2.AuthorizeSecurityGroup(g, ipPerms) 1138 if err != nil && ec2ErrCode(err) == "InvalidPermission.Duplicate" { 1139 if len(ports) == 1 { 1140 return nil 1141 } 1142 // If there's more than one port and we get a duplicate error, 1143 // then we go through authorizing each port individually, 1144 // otherwise the ports that were *not* duplicates will have 1145 // been ignored 1146 for i := range ipPerms { 1147 _, err := e.ec2.AuthorizeSecurityGroup(g, ipPerms[i:i+1]) 1148 if err != nil && ec2ErrCode(err) != "InvalidPermission.Duplicate" { 1149 return fmt.Errorf("cannot open port %v: %v", ipPerms[i], err) 1150 } 1151 } 1152 return nil 1153 } 1154 if err != nil { 1155 return fmt.Errorf("cannot open ports: %v", err) 1156 } 1157 return nil 1158 } 1159 1160 func (e *environ) closePortsInGroup(name string, ports []network.PortRange) error { 1161 if len(ports) == 0 { 1162 return nil 1163 } 1164 // Revoke permissions for anyone to access the given ports. 1165 // Note that ec2 allows the revocation of permissions that aren't 1166 // granted, so this is naturally idempotent. 1167 g, err := e.groupByName(name) 1168 if err != nil { 1169 return err 1170 } 1171 _, err = e.ec2.RevokeSecurityGroup(g, portsToIPPerms(ports)) 1172 if err != nil { 1173 return fmt.Errorf("cannot close ports: %v", err) 1174 } 1175 return nil 1176 } 1177 1178 func (e *environ) portsInGroup(name string) (ports []network.PortRange, err error) { 1179 group, err := e.groupInfoByName(name) 1180 if err != nil { 1181 return nil, err 1182 } 1183 for _, p := range group.IPPerms { 1184 if len(p.SourceIPs) != 1 { 1185 logger.Errorf("expected exactly one IP permission, found: %v", p) 1186 continue 1187 } 1188 ports = append(ports, network.PortRange{ 1189 Protocol: p.Protocol, 1190 FromPort: p.FromPort, 1191 ToPort: p.ToPort, 1192 }) 1193 } 1194 network.SortPortRanges(ports) 1195 return ports, nil 1196 } 1197 1198 func (e *environ) OpenPorts(ports []network.PortRange) error { 1199 if e.Config().FirewallMode() != config.FwGlobal { 1200 return errors.Errorf("invalid firewall mode %q for opening ports on model", e.Config().FirewallMode()) 1201 } 1202 if err := e.openPortsInGroup(e.globalGroupName(), ports); err != nil { 1203 return errors.Trace(err) 1204 } 1205 logger.Infof("opened ports in global group: %v", ports) 1206 return nil 1207 } 1208 1209 func (e *environ) ClosePorts(ports []network.PortRange) error { 1210 if e.Config().FirewallMode() != config.FwGlobal { 1211 return errors.Errorf("invalid firewall mode %q for closing ports on model", e.Config().FirewallMode()) 1212 } 1213 if err := e.closePortsInGroup(e.globalGroupName(), ports); err != nil { 1214 return errors.Trace(err) 1215 } 1216 logger.Infof("closed ports in global group: %v", ports) 1217 return nil 1218 } 1219 1220 func (e *environ) Ports() ([]network.PortRange, error) { 1221 if e.Config().FirewallMode() != config.FwGlobal { 1222 return nil, errors.Errorf("invalid firewall mode %q for retrieving ports from model", e.Config().FirewallMode()) 1223 } 1224 return e.portsInGroup(e.globalGroupName()) 1225 } 1226 1227 func (*environ) Provider() environs.EnvironProvider { 1228 return &providerInstance 1229 } 1230 1231 func (e *environ) instanceSecurityGroups(instIDs []instance.Id, states ...string) ([]ec2.SecurityGroup, error) { 1232 strInstID := make([]string, len(instIDs)) 1233 for i := range instIDs { 1234 strInstID[i] = string(instIDs[i]) 1235 } 1236 1237 filter := ec2.NewFilter() 1238 if len(states) > 0 { 1239 filter.Add("instance-state-name", states...) 1240 } 1241 1242 resp, err := e.ec2.Instances(strInstID, filter) 1243 if err != nil { 1244 return nil, errors.Annotatef(err, "cannot retrieve instance information from aws to delete security groups") 1245 } 1246 1247 securityGroups := []ec2.SecurityGroup{} 1248 for _, res := range resp.Reservations { 1249 for _, inst := range res.Instances { 1250 logger.Debugf("instance %q has security groups %+v", inst.InstanceId, inst.SecurityGroups) 1251 securityGroups = append(securityGroups, inst.SecurityGroups...) 1252 } 1253 } 1254 return securityGroups, nil 1255 } 1256 1257 // controllerSecurityGroups returns the details of all security groups managed 1258 // by the environment's controller. 1259 func (e *environ) controllerSecurityGroups(controllerUUID string) ([]ec2.SecurityGroup, error) { 1260 filter := ec2.NewFilter() 1261 e.addControllerFilter(filter, controllerUUID) 1262 resp, err := e.ec2.SecurityGroups(nil, filter) 1263 if err != nil { 1264 return nil, errors.Annotate(err, "listing security groups") 1265 } 1266 groups := make([]ec2.SecurityGroup, len(resp.Groups)) 1267 for i, info := range resp.Groups { 1268 groups[i] = ec2.SecurityGroup{Id: info.Id, Name: info.Name} 1269 } 1270 return groups, nil 1271 } 1272 1273 // cleanEnvironmentSecurityGroups attempts to delete all security groups owned 1274 // by the environment. 1275 func (e *environ) cleanEnvironmentSecurityGroups() error { 1276 jujuGroup := e.jujuGroupName() 1277 g, err := e.groupByName(jujuGroup) 1278 if isNotFoundError(err) { 1279 return nil 1280 } 1281 if err != nil { 1282 return errors.Annotatef(err, "cannot retrieve default security group: %q", jujuGroup) 1283 } 1284 if err := deleteSecurityGroupInsistently(e.ec2, g, clock.WallClock); err != nil { 1285 return errors.Annotate(err, "cannot delete default security group") 1286 } 1287 return nil 1288 } 1289 1290 func (e *environ) terminateInstances(ids []instance.Id) error { 1291 if len(ids) == 0 { 1292 return nil 1293 } 1294 1295 // TODO (anastasiamac 2016-04-11) Err if instances still have resources hanging around. 1296 // LP#1568654 1297 defer func() { 1298 e.deleteSecurityGroupsForInstances(ids) 1299 }() 1300 1301 // TODO (anastasiamac 2016-04-7) instance termination would benefit 1302 // from retry with exponential delay just like security groups 1303 // in defer. Bug#1567179. 1304 var err error 1305 for a := shortAttempt.Start(); a.Next(); { 1306 _, err = terminateInstancesById(e.ec2, ids...) 1307 if err == nil || ec2ErrCode(err) != "InvalidInstanceID.NotFound" { 1308 // This will return either success at terminating all instances (1st condition) or 1309 // encountered error as long as it's not NotFound (2nd condition). 1310 return err 1311 } 1312 } 1313 1314 // We will get here only if we got a NotFound error. 1315 // 1. If we attempted to terminate only one instance was, return now. 1316 if len(ids) == 1 { 1317 ids = nil 1318 return nil 1319 } 1320 // 2. If we attempted to terminate several instances and got a NotFound error, 1321 // it means that no instances were terminated. 1322 // So try each instance individually, ignoring a NotFound error this time. 1323 deletedIDs := []instance.Id{} 1324 for _, id := range ids { 1325 _, err = terminateInstancesById(e.ec2, id) 1326 if err == nil { 1327 deletedIDs = append(deletedIDs, id) 1328 } 1329 if err != nil && ec2ErrCode(err) != "InvalidInstanceID.NotFound" { 1330 ids = deletedIDs 1331 return err 1332 } 1333 } 1334 // We will get here if all of the instances are deleted successfully, 1335 // or are not found, which implies they were previously deleted. 1336 ids = deletedIDs 1337 return nil 1338 } 1339 1340 var terminateInstancesById = func(ec2inst *ec2.EC2, ids ...instance.Id) (*ec2.TerminateInstancesResp, error) { 1341 strs := make([]string, len(ids)) 1342 for i, id := range ids { 1343 strs[i] = string(id) 1344 } 1345 return ec2inst.TerminateInstances(strs) 1346 } 1347 1348 func (e *environ) deleteSecurityGroupsForInstances(ids []instance.Id) { 1349 if len(ids) == 0 { 1350 logger.Debugf("no need to delete security groups: no intances were terminated successfully") 1351 return 1352 } 1353 1354 // We only want to attempt deleting security groups for the 1355 // instances that have been successfully terminated. 1356 securityGroups, err := e.instanceSecurityGroups(ids, "shutting-down", "terminated") 1357 if err != nil { 1358 logger.Errorf("cannot determine security groups to delete: %v", err) 1359 return 1360 } 1361 1362 // TODO(perrito666) we need to tag global security groups to be able 1363 // to tell them apart from future groups that are neither machine 1364 // nor environment group. 1365 // https://bugs.launchpad.net/juju-core/+bug/1534289 1366 jujuGroup := e.jujuGroupName() 1367 1368 for _, deletable := range securityGroups { 1369 if deletable.Name == jujuGroup { 1370 continue 1371 } 1372 if err := deleteSecurityGroupInsistently(e.ec2, deletable, clock.WallClock); err != nil { 1373 // In ideal world, we would err out here. 1374 // However: 1375 // 1. We do not know if all instances have been terminated. 1376 // If some instances erred out, they may still be using this security group. 1377 // In this case, our failure to delete security group is reasonable: it's still in use. 1378 // 2. Some security groups may be shared by multiple instances, 1379 // for example, global firewalling. We should not delete these. 1380 logger.Errorf("provider failure: %v", err) 1381 } 1382 } 1383 } 1384 1385 // SecurityGroupCleaner defines provider instance methods needed to delete 1386 // a security group. 1387 type SecurityGroupCleaner interface { 1388 1389 // DeleteSecurityGroup deletes security group on the provider. 1390 DeleteSecurityGroup(group ec2.SecurityGroup) (resp *ec2.SimpleResp, err error) 1391 } 1392 1393 var deleteSecurityGroupInsistently = func(inst SecurityGroupCleaner, group ec2.SecurityGroup, clock clock.Clock) error { 1394 err := retry.Call(retry.CallArgs{ 1395 Attempts: 30, 1396 Delay: time.Second, 1397 MaxDelay: time.Minute, // because 2**29 seconds is beyond reasonable 1398 BackoffFunc: retry.DoubleDelay, 1399 Clock: clock, 1400 Func: func() error { 1401 _, err := inst.DeleteSecurityGroup(group) 1402 if err == nil || isNotFoundError(err) { 1403 logger.Debugf("deleting security group %q", group.Name) 1404 return nil 1405 } 1406 return errors.Trace(err) 1407 }, 1408 NotifyFunc: func(err error, attempt int) { 1409 logger.Debugf("deleting security group %q, attempt %d", group.Name, attempt) 1410 }, 1411 }) 1412 if err != nil { 1413 return errors.Annotatef(err, "cannot delete security group %q: consider deleting it manually", group.Name) 1414 } 1415 return nil 1416 } 1417 1418 func (e *environ) addModelFilter(f *ec2.Filter) { 1419 f.Add(fmt.Sprintf("tag:%s", tags.JujuModel), e.uuid()) 1420 } 1421 1422 func (e *environ) addControllerFilter(f *ec2.Filter, controllerUUID string) { 1423 f.Add(fmt.Sprintf("tag:%s", tags.JujuController), controllerUUID) 1424 } 1425 1426 func (e *environ) uuid() string { 1427 return e.Config().UUID() 1428 } 1429 1430 func (e *environ) globalGroupName() string { 1431 return fmt.Sprintf("%s-global", e.jujuGroupName()) 1432 } 1433 1434 func (e *environ) machineGroupName(machineId string) string { 1435 return fmt.Sprintf("%s-%s", e.jujuGroupName(), machineId) 1436 } 1437 1438 func (e *environ) jujuGroupName() string { 1439 return "juju-" + e.uuid() 1440 } 1441 1442 // setUpGroups creates the security groups for the new machine, and 1443 // returns them. 1444 // 1445 // Instances are tagged with a group so they can be distinguished from 1446 // other instances that might be running on the same EC2 account. In 1447 // addition, a specific machine security group is created for each 1448 // machine, so that its firewall rules can be configured per machine. 1449 func (e *environ) setUpGroups(controllerUUID, machineId string, apiPort int) ([]ec2.SecurityGroup, error) { 1450 1451 // Ensure there's a global group for Juju-related traffic. 1452 jujuGroup, err := e.ensureGroup(controllerUUID, e.jujuGroupName(), 1453 []ec2.IPPerm{{ 1454 Protocol: "tcp", 1455 FromPort: 22, 1456 ToPort: 22, 1457 SourceIPs: []string{"0.0.0.0/0"}, 1458 }, { 1459 Protocol: "tcp", 1460 FromPort: apiPort, 1461 ToPort: apiPort, 1462 SourceIPs: []string{"0.0.0.0/0"}, 1463 }, { 1464 Protocol: "tcp", 1465 FromPort: 0, 1466 ToPort: 65535, 1467 }, { 1468 Protocol: "udp", 1469 FromPort: 0, 1470 ToPort: 65535, 1471 }, { 1472 Protocol: "icmp", 1473 FromPort: -1, 1474 ToPort: -1, 1475 }}, 1476 ) 1477 if err != nil { 1478 return nil, err 1479 } 1480 1481 var machineGroup ec2.SecurityGroup 1482 switch e.Config().FirewallMode() { 1483 case config.FwInstance: 1484 machineGroup, err = e.ensureGroup(controllerUUID, e.machineGroupName(machineId), nil) 1485 case config.FwGlobal: 1486 machineGroup, err = e.ensureGroup(controllerUUID, e.globalGroupName(), nil) 1487 } 1488 if err != nil { 1489 return nil, err 1490 } 1491 return []ec2.SecurityGroup{jujuGroup, machineGroup}, nil 1492 } 1493 1494 // zeroGroup holds the zero security group. 1495 var zeroGroup ec2.SecurityGroup 1496 1497 // securityGroupsByNameOrID calls ec2.SecurityGroups() either with the given 1498 // groupName or with filter by vpc-id and group-name, depending on whether 1499 // vpc-id is empty or not. 1500 func (e *environ) securityGroupsByNameOrID(groupName string) (*ec2.SecurityGroupsResp, error) { 1501 if chosenVPCID := e.ecfg().vpcID(); isVPCIDSet(chosenVPCID) { 1502 // AWS VPC API requires both of these filters (and no 1503 // group names/ids set) for non-default EC2-VPC groups: 1504 filter := ec2.NewFilter() 1505 filter.Add("vpc-id", chosenVPCID) 1506 filter.Add("group-name", groupName) 1507 return e.ec2.SecurityGroups(nil, filter) 1508 } 1509 1510 // EC2-Classic or EC2-VPC with implicit default VPC need to use the 1511 // GroupName.X arguments instead of the filters. 1512 groups := ec2.SecurityGroupNames(groupName) 1513 return e.ec2.SecurityGroups(groups, nil) 1514 } 1515 1516 // ensureGroup returns the security group with name and perms. 1517 // If a group with name does not exist, one will be created. 1518 // If it exists, its permissions are set to perms. 1519 // Any entries in perms without SourceIPs will be granted for 1520 // the named group only. 1521 func (e *environ) ensureGroup(controllerUUID, name string, perms []ec2.IPPerm) (g ec2.SecurityGroup, err error) { 1522 // Specify explicit VPC ID if needed (not for default VPC or EC2-classic). 1523 chosenVPCID := e.ecfg().vpcID() 1524 inVPCLogSuffix := fmt.Sprintf(" (in VPC %q)", chosenVPCID) 1525 if !isVPCIDSet(chosenVPCID) { 1526 chosenVPCID = "" 1527 inVPCLogSuffix = "" 1528 } 1529 1530 resp, err := e.ec2.CreateSecurityGroup(chosenVPCID, name, "juju group") 1531 if err != nil && ec2ErrCode(err) != "InvalidGroup.Duplicate" { 1532 err = errors.Annotatef(err, "creating security group %q%s", name, inVPCLogSuffix) 1533 return zeroGroup, err 1534 } 1535 1536 var have permSet 1537 if err == nil { 1538 g = resp.SecurityGroup 1539 // Tag the created group with the model and controller UUIDs. 1540 cfg := e.Config() 1541 tags := tags.ResourceTags( 1542 names.NewModelTag(cfg.UUID()), 1543 names.NewControllerTag(controllerUUID), 1544 cfg, 1545 ) 1546 if err := tagResources(e.ec2, tags, g.Id); err != nil { 1547 return g, errors.Annotate(err, "tagging security group") 1548 } 1549 logger.Debugf("created security group %q with ID %q%s", name, g.Id, inVPCLogSuffix) 1550 } else { 1551 resp, err := e.securityGroupsByNameOrID(name) 1552 if err != nil { 1553 err = errors.Annotatef(err, "fetching security group %q%s", name, inVPCLogSuffix) 1554 return zeroGroup, err 1555 } 1556 if len(resp.Groups) == 0 { 1557 return zeroGroup, errors.NotFoundf("security group %q%s", name, inVPCLogSuffix) 1558 } 1559 info := resp.Groups[0] 1560 // It's possible that the old group has the wrong 1561 // description here, but if it does it's probably due 1562 // to something deliberately playing games with juju, 1563 // so we ignore it. 1564 g = info.SecurityGroup 1565 have = newPermSetForGroup(info.IPPerms, g) 1566 } 1567 1568 want := newPermSetForGroup(perms, g) 1569 revoke := make(permSet) 1570 for p := range have { 1571 if !want[p] { 1572 revoke[p] = true 1573 } 1574 } 1575 if len(revoke) > 0 { 1576 _, err := e.ec2.RevokeSecurityGroup(g, revoke.ipPerms()) 1577 if err != nil { 1578 err = errors.Annotatef(err, "revoking security group %q%s", g.Id, inVPCLogSuffix) 1579 return zeroGroup, err 1580 } 1581 } 1582 1583 add := make(permSet) 1584 for p := range want { 1585 if !have[p] { 1586 add[p] = true 1587 } 1588 } 1589 if len(add) > 0 { 1590 _, err := e.ec2.AuthorizeSecurityGroup(g, add.ipPerms()) 1591 if err != nil { 1592 err = errors.Annotatef(err, "authorizing security group %q%s", g.Id, inVPCLogSuffix) 1593 return zeroGroup, err 1594 } 1595 } 1596 return g, nil 1597 } 1598 1599 // permKey represents a permission for a group or an ip address range to access 1600 // the given range of ports. Only one of groupId or ipAddr should be non-empty. 1601 type permKey struct { 1602 protocol string 1603 fromPort int 1604 toPort int 1605 groupId string 1606 ipAddr string 1607 } 1608 1609 type permSet map[permKey]bool 1610 1611 // newPermSetForGroup returns a set of all the permissions in the 1612 // given slice of IPPerms. It ignores the name and owner 1613 // id in source groups, and any entry with no source ips will 1614 // be granted for the given group only. 1615 func newPermSetForGroup(ps []ec2.IPPerm, group ec2.SecurityGroup) permSet { 1616 m := make(permSet) 1617 for _, p := range ps { 1618 k := permKey{ 1619 protocol: p.Protocol, 1620 fromPort: p.FromPort, 1621 toPort: p.ToPort, 1622 } 1623 if len(p.SourceIPs) > 0 { 1624 for _, ip := range p.SourceIPs { 1625 k.ipAddr = ip 1626 m[k] = true 1627 } 1628 } else { 1629 k.groupId = group.Id 1630 m[k] = true 1631 } 1632 } 1633 return m 1634 } 1635 1636 // ipPerms returns m as a slice of permissions usable 1637 // with the ec2 package. 1638 func (m permSet) ipPerms() (ps []ec2.IPPerm) { 1639 // We could compact the permissions, but it 1640 // hardly seems worth it. 1641 for p := range m { 1642 ipp := ec2.IPPerm{ 1643 Protocol: p.protocol, 1644 FromPort: p.fromPort, 1645 ToPort: p.toPort, 1646 } 1647 if p.ipAddr != "" { 1648 ipp.SourceIPs = []string{p.ipAddr} 1649 } else { 1650 ipp.SourceGroups = []ec2.UserSecurityGroup{{Id: p.groupId}} 1651 } 1652 ps = append(ps, ipp) 1653 } 1654 return 1655 } 1656 1657 func isZoneOrSubnetConstrainedError(err error) bool { 1658 return isZoneConstrainedError(err) || isSubnetConstrainedError(err) 1659 } 1660 1661 // isZoneConstrainedError reports whether or not the error indicates 1662 // RunInstances failed due to the specified availability zone being 1663 // constrained for the instance type being provisioned, or is 1664 // otherwise unusable for the specific request made. 1665 func isZoneConstrainedError(err error) bool { 1666 switch err := err.(type) { 1667 case *ec2.Error: 1668 switch err.Code { 1669 case "Unsupported", "InsufficientInstanceCapacity": 1670 // A big hammer, but we've now seen several different error messages 1671 // for constrained zones, and who knows how many more there might 1672 // be. If the message contains "Availability Zone", it's a fair 1673 // bet that it's constrained or otherwise unusable. 1674 return strings.Contains(err.Message, "Availability Zone") 1675 case "InvalidInput": 1676 // If the region has a default VPC, then we will receive an error 1677 // if the AZ does not have a default subnet. Until we have proper 1678 // support for networks, we'll skip over these. 1679 return strings.HasPrefix(err.Message, "No default subnet for availability zone") 1680 case "VolumeTypeNotAvailableInZone": 1681 return true 1682 } 1683 } 1684 return false 1685 } 1686 1687 // isSubnetConstrainedError reports whether or not the error indicates 1688 // RunInstances failed due to the specified VPC subnet ID being constrained for 1689 // the instance type being provisioned, or is otherwise unusable for the 1690 // specific request made. 1691 func isSubnetConstrainedError(err error) bool { 1692 switch err := err.(type) { 1693 case *ec2.Error: 1694 switch err.Code { 1695 case "InsufficientFreeAddressesInSubnet", "InsufficientInstanceCapacity": 1696 // Subnet and/or VPC general limits reached. 1697 return true 1698 case "InvalidSubnetID.NotFound": 1699 // This shouldn't happen, as we validate the subnet IDs, but it can 1700 // happen if the user manually deleted the subnet outside of Juju. 1701 return true 1702 } 1703 } 1704 return false 1705 } 1706 1707 // If the err is of type *ec2.Error, ec2ErrCode returns 1708 // its code, otherwise it returns the empty string. 1709 func ec2ErrCode(err error) string { 1710 ec2err, _ := errors.Cause(err).(*ec2.Error) 1711 if ec2err == nil { 1712 return "" 1713 } 1714 return ec2err.Code 1715 } 1716 1717 func (e *environ) AllocateContainerAddresses(hostInstanceID instance.Id, containerTag names.MachineTag, preparedInfo []network.InterfaceInfo) ([]network.InterfaceInfo, error) { 1718 return nil, errors.NotSupportedf("container address allocation") 1719 } 1720 1721 func (e *environ) ReleaseContainerAddresses(interfaces []network.ProviderInterfaceInfo) error { 1722 return errors.NotSupportedf("container address allocation") 1723 } 1724 1725 func (e *environ) supportedInstanceTypes() ([]instances.InstanceType, error) { 1726 allInstanceTypes := ec2instancetypes.RegionInstanceTypes(e.cloud.Region) 1727 if isVPCIDSet(e.ecfg().vpcID()) { 1728 return allInstanceTypes, nil 1729 } 1730 hasDefaultVPC, err := e.hasDefaultVPC() 1731 if err != nil { 1732 return nil, errors.Trace(err) 1733 } 1734 if hasDefaultVPC { 1735 return allInstanceTypes, nil 1736 } 1737 1738 // The region has no default VPC, and the user has not specified 1739 // one to use. We filter out any instance types that are not 1740 // supported in EC2-Classic. 1741 supportedInstanceTypes := make([]instances.InstanceType, 0, len(allInstanceTypes)) 1742 for _, instanceType := range allInstanceTypes { 1743 if !ec2instancetypes.SupportsClassic(instanceType.Name) { 1744 continue 1745 } 1746 supportedInstanceTypes = append(supportedInstanceTypes, instanceType) 1747 } 1748 return supportedInstanceTypes, nil 1749 } 1750 1751 func (e *environ) hasDefaultVPC() (bool, error) { 1752 e.defaultVPCMutex.Lock() 1753 defer e.defaultVPCMutex.Unlock() 1754 if !e.defaultVPCChecked { 1755 filter := ec2.NewFilter() 1756 filter.Add("isDefault", "true") 1757 resp, err := e.ec2.VPCs(nil, filter) 1758 if err != nil { 1759 return false, errors.Trace(err) 1760 } 1761 if len(resp.VPCs) > 0 { 1762 e.defaultVPC = &resp.VPCs[0] 1763 } 1764 e.defaultVPCChecked = true 1765 } 1766 return e.defaultVPC != nil, nil 1767 }