Use EC2's tag-on-create for various resources

This updates DHCP Options, EBS Volumes, InternetGateways, SecurityGroups, Subnets, and VPCs to specify tags at creation-time rather than calling ec2.CreateTags after the resource was created.

I didn't update NATGateway because it adds additional legacy tags that should be evaluated for whether or not they're needed.
I also didn't update SSHKey because it currently isn't tagged at all but it would be good for us to tag down the road.
This commit is contained in:
Peter Rifel 2020-07-06 23:27:14 -05:00
parent 856d5815a3
commit aadff94bc8
No known key found for this signature in database
GPG Key ID: 30DB43602027D941
10 changed files with 107 additions and 52 deletions

View File

@ -141,7 +141,9 @@ func (_ *DHCPOptions) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *DHCPOption
if a == nil {
klog.V(2).Infof("Creating DHCPOptions with Name:%q", *e.Name)
request := &ec2.CreateDhcpOptionsInput{}
request := &ec2.CreateDhcpOptionsInput{
TagSpecifications: awsup.EC2TagSpecification(ec2.ResourceTypeDhcpOptions, e.Tags),
}
if e.DomainNameServers != nil {
o := &ec2.NewDhcpConfiguration{
Key: aws.String("domain-name-servers"),

View File

@ -24,7 +24,6 @@ import (
"k8s.io/kops/upup/pkg/fi/cloudup/cloudformation"
"k8s.io/kops/upup/pkg/fi/cloudup/terraform"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"k8s.io/klog"
)
@ -140,25 +139,13 @@ func (_ *EBSVolume) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *EBSVolume) e
klog.V(2).Infof("Creating PersistentVolume with Name:%q", *e.Name)
request := &ec2.CreateVolumeInput{
Size: e.SizeGB,
AvailabilityZone: e.AvailabilityZone,
VolumeType: e.VolumeType,
KmsKeyId: e.KmsKeyId,
Encrypted: e.Encrypted,
Iops: e.VolumeIops,
}
if len(e.Tags) != 0 {
request.TagSpecifications = []*ec2.TagSpecification{
{ResourceType: aws.String(ec2.ResourceTypeVolume)},
}
for k, v := range e.Tags {
request.TagSpecifications[0].Tags = append(request.TagSpecifications[0].Tags, &ec2.Tag{
Key: aws.String(k),
Value: aws.String(v),
})
}
Size: e.SizeGB,
AvailabilityZone: e.AvailabilityZone,
VolumeType: e.VolumeType,
KmsKeyId: e.KmsKeyId,
Encrypted: e.Encrypted,
Iops: e.VolumeIops,
TagSpecifications: awsup.EC2TagSpecification(ec2.ResourceTypeVolume, e.Tags),
}
response, err := t.Cloud.EC2().CreateVolume(request)

View File

@ -149,7 +149,9 @@ func (_ *InternetGateway) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *Intern
if a == nil {
klog.V(2).Infof("Creating InternetGateway")
request := &ec2.CreateInternetGatewayInput{}
request := &ec2.CreateInternetGatewayInput{
TagSpecifications: awsup.EC2TagSpecification(ec2.ResourceTypeInternetGateway, e.Tags),
}
response, err := t.Cloud.EC2().CreateInternetGateway(request)
if err != nil {

View File

@ -166,9 +166,10 @@ func (_ *SecurityGroup) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *Security
klog.V(2).Infof("Creating SecurityGroup with Name:%q VPC:%q", *e.Name, *e.VPC.ID)
request := &ec2.CreateSecurityGroupInput{
VpcId: e.VPC.ID,
GroupName: e.Name,
Description: e.Description,
VpcId: e.VPC.ID,
GroupName: e.Name,
Description: e.Description,
TagSpecifications: awsup.EC2TagSpecification(ec2.ResourceTypeSecurityGroup, e.Tags),
}
response, err := t.Cloud.EC2().CreateSecurityGroup(request)

View File

@ -149,8 +149,13 @@ func TestSecurityGroupCreate(t *testing.T) {
Description: s("Description"),
GroupId: sg1.ID,
VpcId: vpc1.ID,
Tags: []*ec2.Tag{},
GroupName: s("sg1"),
Tags: []*ec2.Tag{
{
Key: aws.String("Name"),
Value: aws.String("sg1"),
},
},
GroupName: s("sg1"),
}
actual := c.SecurityGroups[*sg1.ID]
if !reflect.DeepEqual(actual, expected) {

View File

@ -181,9 +181,10 @@ func (_ *Subnet) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *Subnet) error {
klog.V(2).Infof("Creating Subnet with CIDR: %q", *e.CIDR)
request := &ec2.CreateSubnetInput{
CidrBlock: e.CIDR,
AvailabilityZone: e.AvailabilityZone,
VpcId: e.VPC.ID,
CidrBlock: e.CIDR,
AvailabilityZone: e.AvailabilityZone,
VpcId: e.VPC.ID,
TagSpecifications: awsup.EC2TagSpecification(ec2.ResourceTypeSubnet, e.Tags),
}
response, err := t.Cloud.EC2().CreateSubnet(request)

View File

@ -145,42 +145,39 @@ func TestSharedSubnetCreateDoesNotCreateNew(t *testing.T) {
// Pre-create the vpc / subnet
vpc, err := c.CreateVpc(&ec2.CreateVpcInput{
CidrBlock: aws.String("172.20.0.0/16"),
})
if err != nil {
t.Fatalf("error creating test VPC: %v", err)
}
_, err = c.CreateTags(&ec2.CreateTagsInput{
Resources: []*string{vpc.Vpc.VpcId},
Tags: []*ec2.Tag{
TagSpecifications: []*ec2.TagSpecification{
{
Key: aws.String("Name"),
Value: aws.String("ExistingVPC"),
ResourceType: aws.String(ec2.ResourceTypeVpc),
Tags: []*ec2.Tag{
{
Key: aws.String("Name"),
Value: aws.String("ExistingVPC"),
},
},
},
},
})
if err != nil {
t.Fatalf("error tagging test vpc: %v", err)
t.Fatalf("error creating test VPC: %v", err)
}
subnet, err := c.CreateSubnet(&ec2.CreateSubnetInput{
VpcId: vpc.Vpc.VpcId,
CidrBlock: aws.String("172.20.1.0/24"),
})
if err != nil {
t.Fatalf("error creating test subnet: %v", err)
}
_, err = c.CreateTags(&ec2.CreateTagsInput{
Resources: []*string{subnet.Subnet.SubnetId},
Tags: []*ec2.Tag{
TagSpecifications: []*ec2.TagSpecification{
{
Key: aws.String("Name"),
Value: aws.String("ExistingSubnet"),
ResourceType: aws.String(ec2.ResourceTypeSubnet),
Tags: []*ec2.Tag{
{
Key: aws.String("Name"),
Value: aws.String("ExistingSubnet"),
},
},
},
},
})
if err != nil {
t.Fatalf("error tagging test subnet: %v", err)
t.Fatalf("error creating test subnet: %v", err)
}
// We define a function so we can rebuild the tasks, because we modify in-place when running

View File

@ -155,7 +155,8 @@ func (_ *VPC) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *VPC) error {
klog.V(2).Infof("Creating VPC with CIDR: %q", *e.CIDR)
request := &ec2.CreateVpcInput{
CidrBlock: e.CIDR,
CidrBlock: e.CIDR,
TagSpecifications: awsup.EC2TagSpecification(ec2.ResourceTypeVpc, e.Tags),
}
response, err := t.Cloud.EC2().CreateVpc(request)

View File

@ -178,3 +178,22 @@ func AWSErrorMessage(err error) string {
}
return ""
}
// EC2TagSpecification converts a map of tags to an EC2 TagSpecification
func EC2TagSpecification(resourceType string, tags map[string]string) []*ec2.TagSpecification {
if len(tags) == 0 {
return nil
}
specification := &ec2.TagSpecification{
ResourceType: aws.String(resourceType),
Tags: make([]*ec2.Tag, 0),
}
for k, v := range tags {
specification.Tags = append(specification.Tags, &ec2.Tag{
Key: aws.String(k),
Value: aws.String(v),
})
}
return []*ec2.TagSpecification{specification}
}

View File

@ -17,6 +17,7 @@ limitations under the License.
package awsup
import (
"reflect"
"testing"
"github.com/aws/aws-sdk-go/aws"
@ -65,3 +66,42 @@ func TestFindRegion(t *testing.T) {
}
}
func TestEC2TagSpecification(t *testing.T) {
cases := []struct {
Name string
ResourceType string
Tags map[string]string
Specification []*ec2.TagSpecification
}{
{
Name: "No tags",
},
{
Name: "simple tag",
ResourceType: "vpc",
Tags: map[string]string{
"foo": "bar",
},
Specification: []*ec2.TagSpecification{
{
ResourceType: aws.String("vpc"),
Tags: []*ec2.Tag{
{
Key: aws.String("foo"),
Value: aws.String("bar"),
},
},
},
},
},
}
for _, tc := range cases {
t.Run(tc.Name, func(t *testing.T) {
s := EC2TagSpecification(tc.ResourceType, tc.Tags)
if !reflect.DeepEqual(s, tc.Specification) {
t.Fatalf("tag specifications did not match: %q vs %q", s, tc.Specification)
}
})
}
}