diff --git a/drivers/amazonec2/amazonec2.go b/drivers/amazonec2/amazonec2.go index b5d1ce0ac95674ebbf5e136be2614fe8dba9d7b8..046cf2d82ed0e17e723be74c5c6aec98e4bd64df 100644 --- a/drivers/amazonec2/amazonec2.go +++ b/drivers/amazonec2/amazonec2.go @@ -8,6 +8,7 @@ import ( "fmt" "io" "io/ioutil" + "math" "net" "net/url" "strconv" @@ -629,9 +630,6 @@ func (d *Driver) Create() error { func (d *Driver) innerCreate() error { log.Infof("Launching instance...") - - tagSpecifications := d.createTagSpecifications() - if err := d.createKeyPair(); err != nil { return fmt.Errorf("unable to create key pair: %s", err) } @@ -671,11 +669,15 @@ func (d *Driver) innerCreate() error { var instance *ec2.Instance + // As mentioned in + // https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/spot-requests.html#concepts-spot-instances-request-tags, + // when you tag a Spot Instance request, the instances and volumes that are launched by the Spot Instance request + // are not automatically tagged. We must explicitly tag the instances and volumes + // launched by the Spot Instance request once they are available req := ec2.RunInstancesInput{ - TagSpecifications: tagSpecifications, - ImageId: &d.AMI, - MinCount: aws.Int64(1), - MaxCount: aws.Int64(1), + ImageId: &d.AMI, + MinCount: aws.Int64(1), + MaxCount: aws.Int64(1), Placement: &ec2.Placement{ AvailabilityZone: ®ionZone, }, @@ -704,7 +706,10 @@ func (d *Driver) innerCreate() error { if d.BlockDurationMinutes != 0 { req.InstanceMarketOptions.SpotOptions.BlockDurationMinutes = &d.BlockDurationMinutes } + } else { + req.TagSpecifications = d.createTagSpecifications() } + inst, err := d.getClient().RunInstances(&req) if err != nil { @@ -717,6 +722,32 @@ func (d *Driver) innerCreate() error { d.InstanceId = *instance.InstanceId + if d.RequestSpotInstance && d.InstanceId != "" { + // According to this AWS Example, + // https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/ec2-example-create-images.html + // You can add up to 10 tags to an instance in a single CreateTags operation + tags := d.createTags() + for { + end := int(math.Min(float64(10), float64(len(tags)))) + t := tags[:end] + + req := ec2.CreateTagsInput{ + Resources: []*string{instance.InstanceId}, + Tags: t, + } + _, err := d.getClient().CreateTags(&req) + if err != nil { + return fmt.Errorf("Error setting tags for the spot instance: %v", err) + } + + tags = tags[end:] + + if len(tags) == 0 { + break + } + } + } + log.Debug("waiting for ip address to become available") if err := mcnutils.WaitFor(d.instanceIpAvailable); err != nil { return err @@ -1018,6 +1049,24 @@ func (d *Driver) securityGroupAvailableFunc(id string) func() bool { } func (d *Driver) createTagSpecifications() []*ec2.TagSpecification { + tags := d.createTags() + return []*ec2.TagSpecification{ + { + ResourceType: aws.String("instance"), + Tags: tags, + }, + { + ResourceType: aws.String("volume"), + Tags: tags, + }, + { + ResourceType: aws.String("network-interface"), + Tags: tags, + }, + } +} + +func (d *Driver) createTags() []*ec2.Tag { var tags []*ec2.Tag tags = append(tags, &ec2.Tag{ Key: aws.String("Name"), @@ -1037,29 +1086,7 @@ func (d *Driver) createTagSpecifications() []*ec2.TagSpecification { } } - if d.RequestSpotInstance { - return []*ec2.TagSpecification{ - { - ResourceType: aws.String("spot-instances-request"), - Tags: tags, - }, - } - } else { - return []*ec2.TagSpecification{ - { - ResourceType: aws.String("instance"), - Tags: tags, - }, - { - ResourceType: aws.String("volume"), - Tags: tags, - }, - { - ResourceType: aws.String("network-interface"), - Tags: tags, - }, - } - } + return tags } func (d *Driver) getTagResources() []*string {