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