From 59a6b065a088b9bfa8f31e391cd2772f2a5eeca5 Mon Sep 17 00:00:00 2001 From: Ciprian Hacman Date: Mon, 3 Feb 2020 10:46:50 +0200 Subject: [PATCH 1/3] Add root volume tags for CF and TF targets --- pkg/resources/aws/aws.go | 4 +- .../mixed_instances/cloudformation.json | 75 +++++++++- .../mixed_instances/kubernetes.tf | 24 ++++ .../mixed_instances_spot/cloudformation.json | 75 +++++++++- .../mixed_instances_spot/kubernetes.tf | 24 ++++ .../fi/cloudup/awstasks/autoscalinggroup.go | 134 +++++++++++------- .../cloudup/awstasks/autoscalinggroup_test.go | 10 +- .../awstasks/launchtemplate_target_api.go | 22 ++- .../launchtemplate_target_cloudformation.go | 49 +++++-- ...unchtemplate_target_cloudformation_test.go | 40 +++--- .../launchtemplate_target_terraform.go | 34 ++++- 11 files changed, 372 insertions(+), 119 deletions(-) diff --git a/pkg/resources/aws/aws.go b/pkg/resources/aws/aws.go index e0b36b3a72bd7..7b7f002e3dd80 100644 --- a/pkg/resources/aws/aws.go +++ b/pkg/resources/aws/aws.go @@ -1205,8 +1205,8 @@ func FindAutoScalingLaunchTemplateConfigurations(cloud fi.Cloud, securityGroups for _, j := range req.LaunchTemplateVersions { // @check if the security group references the security group above var s []*string - if len(j.LaunchTemplateData.NetworkInterfaces) > 0 { - s = append(s, j.LaunchTemplateData.NetworkInterfaces[0].Groups...) + for _, ni := range j.LaunchTemplateData.NetworkInterfaces { + s = append(s, ni.Groups...) } s = append(s, j.LaunchTemplateData.SecurityGroupIds...) for _, y := range s { diff --git a/tests/integration/update_cluster/mixed_instances/cloudformation.json b/tests/integration/update_cluster/mixed_instances/cloudformation.json index cdde37e0defda..eacb458ee0f23 100644 --- a/tests/integration/update_cluster/mixed_instances/cloudformation.json +++ b/tests/integration/update_cluster/mixed_instances/cloudformation.json @@ -230,7 +230,15 @@ "MixedInstancesPolicy": { "LaunchTemplate": { "LaunchTemplateSpecification": { - "LaunchTemplateName": "nodes.mixedinstances.example.com" + "LaunchTemplateId": { + "Ref": "AWSEC2LaunchTemplatenodesmixedinstancesexamplecom" + }, + "Version": { + "Fn::GetAtt": [ + "AWSEC2LaunchTemplatenodesmixedinstancesexamplecom", + "LatestVersionNumber" + ] + } }, "Overrides": [ { @@ -400,7 +408,7 @@ "BlockDeviceMappings": [ { "DeviceName": "/dev/xvda", - "EBS": { + "Ebs": { "VolumeType": "gp2", "VolumeSize": 128, "DeleteOnTermination": true @@ -418,15 +426,68 @@ "NetworkInterfaces": [ { "AssociatePublicIpAddress": true, - "DeleteOnTermination": true + "DeleteOnTermination": true, + "DeviceIndex": 0, + "Groups": [ + { + "Ref": "AWSEC2SecurityGroupnodesmixedinstancesexamplecom" + } + ] } ], - "UserData": "extracted", - "SecurityGroup": [ + "TagSpecifications": [ { - "Ref": "AWSEC2SecurityGroupnodesmixedinstancesexamplecom" + "ResourceType": "instance", + "Tags": [ + { + "Key": "KubernetesCluster", + "Value": "mixedinstances.example.com" + }, + { + "Key": "Name", + "Value": "nodes.mixedinstances.example.com" + }, + { + "Key": "k8s.io/role/node", + "Value": "1" + }, + { + "Key": "kops.k8s.io/instancegroup", + "Value": "nodes" + }, + { + "Key": "kubernetes.io/cluster/mixedinstances.example.com", + "Value": "owned" + } + ] + }, + { + "ResourceType": "volume", + "Tags": [ + { + "Key": "KubernetesCluster", + "Value": "mixedinstances.example.com" + }, + { + "Key": "Name", + "Value": "nodes.mixedinstances.example.com" + }, + { + "Key": "k8s.io/role/node", + "Value": "1" + }, + { + "Key": "kops.k8s.io/instancegroup", + "Value": "nodes" + }, + { + "Key": "kubernetes.io/cluster/mixedinstances.example.com", + "Value": "owned" + } + ] } - ] + ], + "UserData": "extracted" } } }, diff --git a/tests/integration/update_cluster/mixed_instances/kubernetes.tf b/tests/integration/update_cluster/mixed_instances/kubernetes.tf index ae2b622598b0d..3831894bbad10 100644 --- a/tests/integration/update_cluster/mixed_instances/kubernetes.tf +++ b/tests/integration/update_cluster/mixed_instances/kubernetes.tf @@ -533,6 +533,30 @@ resource "aws_launch_template" "nodes-mixedinstances-example-com" { security_groups = ["${aws_security_group.nodes-mixedinstances-example-com.id}"] } + tag_specifications = { + resource_type = "instance" + + tags = { + KubernetesCluster = "mixedinstances.example.com" + Name = "nodes.mixedinstances.example.com" + "k8s.io/role/node" = "1" + "kops.k8s.io/instancegroup" = "nodes" + "kubernetes.io/cluster/mixedinstances.example.com" = "owned" + } + } + + tag_specifications = { + resource_type = "volume" + + tags = { + KubernetesCluster = "mixedinstances.example.com" + Name = "nodes.mixedinstances.example.com" + "k8s.io/role/node" = "1" + "kops.k8s.io/instancegroup" = "nodes" + "kubernetes.io/cluster/mixedinstances.example.com" = "owned" + } + } + user_data = "${file("${path.module}/data/aws_launch_template_nodes.mixedinstances.example.com_user_data")}" } diff --git a/tests/integration/update_cluster/mixed_instances_spot/cloudformation.json b/tests/integration/update_cluster/mixed_instances_spot/cloudformation.json index 18beb0ff16c53..c39725c9e86db 100644 --- a/tests/integration/update_cluster/mixed_instances_spot/cloudformation.json +++ b/tests/integration/update_cluster/mixed_instances_spot/cloudformation.json @@ -230,7 +230,15 @@ "MixedInstancesPolicy": { "LaunchTemplate": { "LaunchTemplateSpecification": { - "LaunchTemplateName": "nodes.mixedinstances.example.com" + "LaunchTemplateId": { + "Ref": "AWSEC2LaunchTemplatenodesmixedinstancesexamplecom" + }, + "Version": { + "Fn::GetAtt": [ + "AWSEC2LaunchTemplatenodesmixedinstancesexamplecom", + "LatestVersionNumber" + ] + } }, "Overrides": [ { @@ -401,7 +409,7 @@ "BlockDeviceMappings": [ { "DeviceName": "/dev/xvda", - "EBS": { + "Ebs": { "VolumeType": "gp2", "VolumeSize": 128, "DeleteOnTermination": true @@ -419,15 +427,68 @@ "NetworkInterfaces": [ { "AssociatePublicIpAddress": true, - "DeleteOnTermination": true + "DeleteOnTermination": true, + "DeviceIndex": 0, + "Groups": [ + { + "Ref": "AWSEC2SecurityGroupnodesmixedinstancesexamplecom" + } + ] } ], - "UserData": "extracted", - "SecurityGroup": [ + "TagSpecifications": [ { - "Ref": "AWSEC2SecurityGroupnodesmixedinstancesexamplecom" + "ResourceType": "instance", + "Tags": [ + { + "Key": "KubernetesCluster", + "Value": "mixedinstances.example.com" + }, + { + "Key": "Name", + "Value": "nodes.mixedinstances.example.com" + }, + { + "Key": "k8s.io/role/node", + "Value": "1" + }, + { + "Key": "kops.k8s.io/instancegroup", + "Value": "nodes" + }, + { + "Key": "kubernetes.io/cluster/mixedinstances.example.com", + "Value": "owned" + } + ] + }, + { + "ResourceType": "volume", + "Tags": [ + { + "Key": "KubernetesCluster", + "Value": "mixedinstances.example.com" + }, + { + "Key": "Name", + "Value": "nodes.mixedinstances.example.com" + }, + { + "Key": "k8s.io/role/node", + "Value": "1" + }, + { + "Key": "kops.k8s.io/instancegroup", + "Value": "nodes" + }, + { + "Key": "kubernetes.io/cluster/mixedinstances.example.com", + "Value": "owned" + } + ] } - ] + ], + "UserData": "extracted" } } }, diff --git a/tests/integration/update_cluster/mixed_instances_spot/kubernetes.tf b/tests/integration/update_cluster/mixed_instances_spot/kubernetes.tf index 72b6b10792345..6f0b0c7a91535 100644 --- a/tests/integration/update_cluster/mixed_instances_spot/kubernetes.tf +++ b/tests/integration/update_cluster/mixed_instances_spot/kubernetes.tf @@ -533,6 +533,30 @@ resource "aws_launch_template" "nodes-mixedinstances-example-com" { security_groups = ["${aws_security_group.nodes-mixedinstances-example-com.id}"] } + tag_specifications = { + resource_type = "instance" + + tags = { + KubernetesCluster = "mixedinstances.example.com" + Name = "nodes.mixedinstances.example.com" + "k8s.io/role/node" = "1" + "kops.k8s.io/instancegroup" = "nodes" + "kubernetes.io/cluster/mixedinstances.example.com" = "owned" + } + } + + tag_specifications = { + resource_type = "volume" + + tags = { + KubernetesCluster = "mixedinstances.example.com" + Name = "nodes.mixedinstances.example.com" + "k8s.io/role/node" = "1" + "kops.k8s.io/instancegroup" = "nodes" + "kubernetes.io/cluster/mixedinstances.example.com" = "owned" + } + } + user_data = "${file("${path.module}/data/aws_launch_template_nodes.mixedinstances.example.com_user_data")}" } diff --git a/upup/pkg/fi/cloudup/awstasks/autoscalinggroup.go b/upup/pkg/fi/cloudup/awstasks/autoscalinggroup.go index 02b8b8cdd52e5..a0f138e721596 100644 --- a/upup/pkg/fi/cloudup/awstasks/autoscalinggroup.go +++ b/upup/pkg/fi/cloudup/awstasks/autoscalinggroup.go @@ -263,21 +263,10 @@ func (v *AutoscalingGroup) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *Autos VPCZoneIdentifier: fi.String(strings.Join(e.AutoscalingGroupSubnets(), ",")), } - // @check are we using a launchconfiguation + // @check are we using a launch configuation or mixed instance policies or launch template if e.LaunchConfiguration != nil { request.LaunchConfigurationName = e.LaunchConfiguration.ID - } - // @check are we using launch template - if e.LaunchTemplate != nil { - request.LaunchTemplate = &autoscaling.LaunchTemplateSpecification{ - LaunchTemplateName: e.LaunchTemplate.ID, - } - } - - // @check if we are using mixed instance policies - if e.UseMixedInstancesPolicy() { - // we can zero this out for now and use the mixed instance policy for definition - request.LaunchTemplate = nil + } else if e.UseMixedInstancesPolicy() { request.MixedInstancesPolicy = &autoscaling.MixedInstancesPolicy{ InstancesDistribution: &autoscaling.InstancesDistribution{ OnDemandPercentageAboveBaseCapacity: e.MixedOnDemandAboveBase, @@ -287,13 +276,25 @@ func (v *AutoscalingGroup) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *Autos SpotMaxPrice: e.MixedSpotMaxPrice, }, LaunchTemplate: &autoscaling.LaunchTemplate{ - LaunchTemplateSpecification: &autoscaling.LaunchTemplateSpecification{LaunchTemplateName: e.LaunchTemplate.ID, Version: aws.String("1")}, + LaunchTemplateSpecification: &autoscaling.LaunchTemplateSpecification{ + LaunchTemplateName: e.LaunchTemplate.ID, + Version: aws.String("1"), + }, }, } p := request.MixedInstancesPolicy.LaunchTemplate for _, x := range e.MixedInstanceOverrides { - p.Overrides = append(p.Overrides, &autoscaling.LaunchTemplateOverrides{InstanceType: fi.String(x)}) + p.Overrides = append(p.Overrides, &autoscaling.LaunchTemplateOverrides{ + InstanceType: fi.String(x)}, + ) + } + } else if e.LaunchTemplate != nil { + request.LaunchTemplate = &autoscaling.LaunchTemplateSpecification{ + LaunchTemplateName: e.LaunchTemplate.ID, + Version: aws.String("1"), } + } else { + return fmt.Errorf("could not find on of launch configuation, mixed instance policies or launch template") } // @step: attempt to create the autoscaling group for us @@ -610,22 +611,29 @@ type terraformASGTag struct { } type terraformAutoscalingLaunchTemplateSpecification struct { + // LaunchTemplateID is the ID of the template to use + LaunchTemplateID *terraform.Literal `json:"id,omitempty"` + // Version is the version of the Launch Template to use + Version *terraform.Literal `json:"version,omitempty"` +} + +type terraformAutoscalingMixedInstancesPolicyLaunchTemplateSpecification struct { // LaunchTemplateID is the ID of the template to use LaunchTemplateID *terraform.Literal `json:"launch_template_id,omitempty"` // Version is the version of the Launch Template to use Version *terraform.Literal `json:"version,omitempty"` } -type terraformAutoscalingLaunchTemplateOverride struct { +type terraformAutoscalingMixedInstancesPolicyLaunchTemplateOverride struct { // InstanceType is the instance to use InstanceType *string `json:"instance_type,omitempty"` } -type terraformAutoscalingLaunchTemplate struct { +type terraformAutoscalingMixedInstancesPolicyLaunchTemplate struct { // LaunchTemplateSpecification is the definition for a LT - LaunchTemplateSpecification []*terraformAutoscalingLaunchTemplateSpecification `json:"launch_template_specification,omitempty"` + LaunchTemplateSpecification []*terraformAutoscalingMixedInstancesPolicyLaunchTemplateSpecification `json:"launch_template_specification,omitempty"` // Override the is machine type override - Override []*terraformAutoscalingLaunchTemplateOverride `json:"override,omitempty"` + Override []*terraformAutoscalingMixedInstancesPolicyLaunchTemplateOverride `json:"override,omitempty"` } type terraformAutoscalingInstanceDistribution struct { @@ -645,23 +653,24 @@ type terraformAutoscalingInstanceDistribution struct { type terraformMixedInstancesPolicy struct { // LaunchTemplate is the launch template spec - LaunchTemplate []*terraformAutoscalingLaunchTemplate `json:"launch_template,omitempty"` + LaunchTemplate []*terraformAutoscalingMixedInstancesPolicyLaunchTemplate `json:"launch_template,omitempty"` // InstanceDistribution is the distribution strategy InstanceDistribution []*terraformAutoscalingInstanceDistribution `json:"instances_distribution,omitempty"` } type terraformAutoscalingGroup struct { - Name *string `json:"name,omitempty"` - LaunchConfigurationName *terraform.Literal `json:"launch_configuration,omitempty"` - MaxSize *int64 `json:"max_size,omitempty"` - MinSize *int64 `json:"min_size,omitempty"` - MixedInstancesPolicy []*terraformMixedInstancesPolicy `json:"mixed_instances_policy,omitempty"` - VPCZoneIdentifier []*terraform.Literal `json:"vpc_zone_identifier,omitempty"` - Tags []*terraformASGTag `json:"tag,omitempty"` - MetricsGranularity *string `json:"metrics_granularity,omitempty"` - EnabledMetrics []*string `json:"enabled_metrics,omitempty"` - SuspendedProcesses []*string `json:"suspended_processes,omitempty"` - InstanceProtection *bool `json:"protect_from_scale_in,omitempty"` + Name *string `json:"name,omitempty"` + LaunchConfigurationName *terraform.Literal `json:"launch_configuration,omitempty"` + LaunchTemplate *terraformAutoscalingLaunchTemplateSpecification `json:"launch_template,omitempty"` + MaxSize *int64 `json:"max_size,omitempty"` + MinSize *int64 `json:"min_size,omitempty"` + MixedInstancesPolicy []*terraformMixedInstancesPolicy `json:"mixed_instances_policy,omitempty"` + VPCZoneIdentifier []*terraform.Literal `json:"vpc_zone_identifier,omitempty"` + Tags []*terraformASGTag `json:"tag,omitempty"` + MetricsGranularity *string `json:"metrics_granularity,omitempty"` + EnabledMetrics []*string `json:"enabled_metrics,omitempty"` + SuspendedProcesses []*string `json:"suspended_processes,omitempty"` + InstanceProtection *bool `json:"protect_from_scale_in,omitempty"` } // RenderTerraform is responsible for rendering the terraform codebase @@ -688,7 +697,9 @@ func (_ *AutoscalingGroup) RenderTerraform(t *terraform.TerraformTarget, a, e, c }) } - if e.UseMixedInstancesPolicy() { + if e.LaunchConfiguration != nil { + tf.LaunchConfigurationName = e.LaunchConfiguration.TerraformLink() + } else if e.UseMixedInstancesPolicy() { // Temporary warning until https://github.com/terraform-providers/terraform-provider-aws/issues/9750 is resolved if e.MixedSpotAllocationStrategy == fi.String("capacity-optimized") { fmt.Print("Terraform does not currently support a capacity optimized strategy - please see https://github.com/terraform-providers/terraform-provider-aws/issues/9750") @@ -696,9 +707,9 @@ func (_ *AutoscalingGroup) RenderTerraform(t *terraform.TerraformTarget, a, e, c tf.MixedInstancesPolicy = []*terraformMixedInstancesPolicy{ { - LaunchTemplate: []*terraformAutoscalingLaunchTemplate{ + LaunchTemplate: []*terraformAutoscalingMixedInstancesPolicyLaunchTemplate{ { - LaunchTemplateSpecification: []*terraformAutoscalingLaunchTemplateSpecification{ + LaunchTemplateSpecification: []*terraformAutoscalingMixedInstancesPolicyLaunchTemplateSpecification{ { LaunchTemplateID: e.LaunchTemplate.TerraformLink(), Version: e.LaunchTemplate.VersionLink(), @@ -720,8 +731,15 @@ func (_ *AutoscalingGroup) RenderTerraform(t *terraform.TerraformTarget, a, e, c } for _, x := range e.MixedInstanceOverrides { - tf.MixedInstancesPolicy[0].LaunchTemplate[0].Override = append(tf.MixedInstancesPolicy[0].LaunchTemplate[0].Override, &terraformAutoscalingLaunchTemplateOverride{InstanceType: fi.String(x)}) + tf.MixedInstancesPolicy[0].LaunchTemplate[0].Override = append(tf.MixedInstancesPolicy[0].LaunchTemplate[0].Override, &terraformAutoscalingMixedInstancesPolicyLaunchTemplateOverride{InstanceType: fi.String(x)}) + } + } else if e.LaunchTemplate != nil { + tf.LaunchTemplate = &terraformAutoscalingLaunchTemplateSpecification{ + LaunchTemplateID: e.LaunchTemplate.TerraformLink(), + Version: e.LaunchTemplate.VersionLink(), } + } else { + return fmt.Errorf("could not find on of launch configuation, mixed instance policies or launch template") } role := "" @@ -736,7 +754,6 @@ func (_ *AutoscalingGroup) RenderTerraform(t *terraform.TerraformTarget, a, e, c } if e.LaunchConfiguration != nil { - tf.LaunchConfigurationName = e.LaunchConfiguration.TerraformLink() // Create TF output variable with security group ids // This is in the launch configuration, but the ASG has the information about the instance group type if role != "" { @@ -794,8 +811,10 @@ type cloudformationASGMetricsCollection struct { } type cloudformationAutoscalingLaunchTemplateSpecification struct { - // LaunchTemplateName is the name of the template to use - LaunchTemplateName *string `json:"LaunchTemplateName,omitempty"` + // LaunchTemplateId is the IDx of the template to use + LaunchTemplateId *cloudformation.Literal `json:"LaunchTemplateId,omitempty"` + // Version is the version number of the template to use + Version *cloudformation.Literal `json:"Version,omitempty"` } type cloudformationAutoscalingLaunchTemplateOverride struct { @@ -833,16 +852,17 @@ type cloudformationMixedInstancesPolicy struct { } type cloudformationAutoscalingGroup struct { - Name *string `json:"AutoScalingGroupName,omitempty"` - LaunchConfigurationName *cloudformation.Literal `json:"LaunchConfigurationName,omitempty"` - MaxSize *int64 `json:"MaxSize,omitempty"` - MinSize *int64 `json:"MinSize,omitempty"` - VPCZoneIdentifier []*cloudformation.Literal `json:"VPCZoneIdentifier,omitempty"` - Tags []*cloudformationASGTag `json:"Tags,omitempty"` - MetricsCollection []*cloudformationASGMetricsCollection `json:"MetricsCollection,omitempty"` - MixedInstancesPolicy *cloudformationMixedInstancesPolicy `json:"MixedInstancesPolicy,omitempty"` - LoadBalancerNames []*cloudformation.Literal `json:"LoadBalancerNames,omitempty"` - TargetGroupARNs []*cloudformation.Literal `json:"TargetGroupARNs,omitempty"` + Name *string `json:"AutoScalingGroupName,omitempty"` + LaunchConfigurationName *cloudformation.Literal `json:"LaunchConfigurationName,omitempty"` + LaunchTemplate *cloudformationAutoscalingLaunchTemplateSpecification `json:"LaunchTemplate,omitempty"` + MaxSize *int64 `json:"MaxSize,omitempty"` + MinSize *int64 `json:"MinSize,omitempty"` + VPCZoneIdentifier []*cloudformation.Literal `json:"VPCZoneIdentifier,omitempty"` + Tags []*cloudformationASGTag `json:"Tags,omitempty"` + MetricsCollection []*cloudformationASGMetricsCollection `json:"MetricsCollection,omitempty"` + MixedInstancesPolicy *cloudformationMixedInstancesPolicy `json:"MixedInstancesPolicy,omitempty"` + LoadBalancerNames []*cloudformation.Literal `json:"LoadBalancerNames,omitempty"` + TargetGroupARNs []*cloudformation.Literal `json:"TargetGroupARNs,omitempty"` } // RenderCloudformation is responsible for generating the cloudformation template @@ -859,11 +879,14 @@ func (_ *AutoscalingGroup) RenderCloudformation(t *cloudformation.Cloudformation }, } - if e.UseMixedInstancesPolicy() { + if e.LaunchConfiguration != nil { + cf.LaunchConfigurationName = e.LaunchConfiguration.CloudformationLink() + } else if e.UseMixedInstancesPolicy() { cf.MixedInstancesPolicy = &cloudformationMixedInstancesPolicy{ LaunchTemplate: &cloudformationAutoscalingLaunchTemplate{ LaunchTemplateSpecification: &cloudformationAutoscalingLaunchTemplateSpecification{ - LaunchTemplateName: e.LaunchTemplate.Name, + LaunchTemplateId: e.LaunchTemplate.CloudformationLink(), + Version: e.LaunchTemplate.CloudformationVersion(), }, }, InstanceDistribution: &cloudformationAutoscalingInstanceDistribution{ @@ -879,10 +902,13 @@ func (_ *AutoscalingGroup) RenderCloudformation(t *cloudformation.Cloudformation for _, x := range e.MixedInstanceOverrides { cf.MixedInstancesPolicy.LaunchTemplate.Overrides = append(cf.MixedInstancesPolicy.LaunchTemplate.Overrides, &cloudformationAutoscalingLaunchTemplateOverride{InstanceType: fi.String(x)}) } - } - - if e.LaunchConfiguration != nil { - cf.LaunchConfigurationName = e.LaunchConfiguration.CloudformationLink() + } else if e.LaunchTemplate != nil { + cf.LaunchTemplate = &cloudformationAutoscalingLaunchTemplateSpecification{ + LaunchTemplateId: e.LaunchTemplate.CloudformationLink(), + Version: e.LaunchTemplate.CloudformationVersion(), + } + } else { + return fmt.Errorf("could not find on of launch configuation, mixed instance policies or launch template") } for _, s := range e.Subnets { diff --git a/upup/pkg/fi/cloudup/awstasks/autoscalinggroup_test.go b/upup/pkg/fi/cloudup/awstasks/autoscalinggroup_test.go index 4096ac9d665c5..5f9fd32a34305 100644 --- a/upup/pkg/fi/cloudup/awstasks/autoscalinggroup_test.go +++ b/upup/pkg/fi/cloudup/awstasks/autoscalinggroup_test.go @@ -390,7 +390,15 @@ func TestAutoscalingGroupCloudformationRender(t *testing.T) { "MixedInstancesPolicy": { "LaunchTemplate": { "LaunchTemplateSpecification": { - "LaunchTemplateName": "test_lt" + "LaunchTemplateId": { + "Ref": "AWSEC2LaunchTemplatetest_lt" + }, + "Version": { + "Fn::GetAtt": [ + "AWSEC2LaunchTemplatetest_lt", + "LatestVersionNumber" + ] + } }, "Overrides": [ { diff --git a/upup/pkg/fi/cloudup/awstasks/launchtemplate_target_api.go b/upup/pkg/fi/cloudup/awstasks/launchtemplate_target_api.go index 4665d8115e29e..7932f97fe8c86 100644 --- a/upup/pkg/fi/cloudup/awstasks/launchtemplate_target_api.go +++ b/upup/pkg/fi/cloudup/awstasks/launchtemplate_target_api.go @@ -48,6 +48,13 @@ func (t *LaunchTemplate) RenderAWS(c *awsup.AWSAPITarget, a, ep, changes *Launch EbsOptimized: t.RootVolumeOptimization, ImageId: image.ImageId, InstanceType: t.InstanceType, + NetworkInterfaces: []*ec2.LaunchTemplateInstanceNetworkInterfaceSpecificationRequest{ + { + AssociatePublicIpAddress: t.AssociatePublicIP, + DeleteOnTermination: aws.Bool(true), + DeviceIndex: fi.Int64(0), + }, + }, }, LaunchTemplateName: aws.String(name), } @@ -76,10 +83,9 @@ func (t *LaunchTemplate) RenderAWS(c *awsup.AWSAPITarget, a, ep, changes *Launch if t.SSHKey != nil { lc.KeyName = t.SSHKey.Name } - var securityGroups []*string // @step: add the security groups for _, sg := range t.SecurityGroups { - securityGroups = append(securityGroups, sg.ID) + lc.NetworkInterfaces[0].Groups = append(lc.NetworkInterfaces[0].Groups, sg.ID) } // @step: add any tenacy details if t.Tenancy != nil { @@ -96,18 +102,6 @@ func (t *LaunchTemplate) RenderAWS(c *awsup.AWSAPITarget, a, ep, changes *Launch Name: t.IAMInstanceProfile.Name, } } - // @step: are the node publicly facing - if fi.BoolValue(t.AssociatePublicIP) { - lc.NetworkInterfaces = append(lc.NetworkInterfaces, - &ec2.LaunchTemplateInstanceNetworkInterfaceSpecificationRequest{ - AssociatePublicIpAddress: t.AssociatePublicIP, - DeleteOnTermination: aws.Bool(true), - DeviceIndex: fi.Int64(0), - Groups: securityGroups, - }) - } else { - lc.SecurityGroupIds = securityGroups - } // @step: add the tags if len(t.Tags) > 0 { var tags []*ec2.Tag diff --git a/upup/pkg/fi/cloudup/awstasks/launchtemplate_target_cloudformation.go b/upup/pkg/fi/cloudup/awstasks/launchtemplate_target_cloudformation.go index 06621bf898e88..9f20e59f6ef0e 100644 --- a/upup/pkg/fi/cloudup/awstasks/launchtemplate_target_cloudformation.go +++ b/upup/pkg/fi/cloudup/awstasks/launchtemplate_target_cloudformation.go @@ -25,11 +25,15 @@ import ( "k8s.io/kops/upup/pkg/fi/cloudup/cloudformation" ) -type cloudformationLaunchTemplateNetworkInterfaces struct { +type cloudformationLaunchTemplateNetworkInterface struct { // AssociatePublicIPAddress associates a public ip address with the network interface. Boolean value. AssociatePublicIPAddress *bool `json:"AssociatePublicIpAddress,omitempty"` // DeleteOnTermination indicates whether the network interface should be destroyed on instance termination. DeleteOnTermination *bool `json:"DeleteOnTermination,omitempty"` + // DeviceIndex is the device index for the network interface attachment. + DeviceIndex *int `json:"DeviceIndex,omitempty"` + // SecurityGroups is a list of security group ids. + SecurityGroups []*cloudformation.Literal `json:"Groups,omitempty"` } type cloudformationLaunchTemplateMonitoring struct { @@ -92,7 +96,14 @@ type cloudformationLaunchTemplateBlockDevice struct { // VirtualName is used for the ephemeral devices VirtualName *string `json:"VirtualName,omitempty"` // EBS defines the ebs spec - EBS *cloudformationLaunchTemplateBlockDeviceEBS `json:"EBS,omitempty"` + EBS *cloudformationLaunchTemplateBlockDeviceEBS `json:"Ebs,omitempty"` +} + +type cloudformationLaunchTemplateTagSpecification struct { + // The type of resource to tag + ResourceType *string `json:"ResourceType,omitempty"` + // The tags to apply to the resource. + Tags []cloudformationTag `json:"Tags,omitempty"` } type cloudformationLaunchTemplateData struct { @@ -113,13 +124,13 @@ type cloudformationLaunchTemplateData struct { // Monitoring are the instance monitoring options Monitoring *cloudformationLaunchTemplateMonitoring `json:"Monitoring,omitempty"` // NetworkInterfaces are the networking options - NetworkInterfaces []*cloudformationLaunchTemplateNetworkInterfaces `json:"NetworkInterfaces,omitempty"` + NetworkInterfaces []*cloudformationLaunchTemplateNetworkInterface `json:"NetworkInterfaces,omitempty"` // Placement are the tenancy options Placement []*cloudformationLaunchTemplatePlacement `json:"Placement,omitempty"` + // TagSpecifications specifies tags to apply to a resource when the resource is created + TagSpecifications []*cloudformationLaunchTemplateTagSpecification `json:"TagSpecifications,omitempty"` // UserData is the user data for the instances UserData *string `json:"UserData,omitempty"` - // VpcSecurityGroupIDs is a list of security group ids - VpcSecurityGroupIDs []*cloudformation.Literal `json:"SecurityGroup,omitempty"` } type cloudformationLaunchTemplate struct { @@ -134,6 +145,11 @@ func (t *LaunchTemplate) CloudformationLink() *cloudformation.Literal { return cloudformation.Ref("AWS::EC2::LaunchTemplate", fi.StringValue(t.Name)) } +// CloudformationLink returns the cloudformation version for us +func (t *LaunchTemplate) CloudformationVersion() *cloudformation.Literal { + return cloudformation.GetAtt("AWS::EC2::LaunchTemplate", fi.StringValue(t.Name), "LatestVersionNumber") +} + // RenderCloudformation is responsible for rendering the cloudformation json func (t *LaunchTemplate) RenderCloudformation(target *cloudformation.CloudformationTarget, a, e, changes *LaunchTemplate) error { var err error @@ -155,9 +171,12 @@ func (t *LaunchTemplate) RenderCloudformation(target *cloudformation.Cloudformat EBSOptimized: e.RootVolumeOptimization, ImageID: image, InstanceType: e.InstanceType, - NetworkInterfaces: []*cloudformationLaunchTemplateNetworkInterfaces{ - {AssociatePublicIPAddress: e.AssociatePublicIP, - DeleteOnTermination: fi.Bool(true)}, + NetworkInterfaces: []*cloudformationLaunchTemplateNetworkInterface{ + { + AssociatePublicIPAddress: e.AssociatePublicIP, + DeleteOnTermination: fi.Bool(true), + DeviceIndex: fi.Int(0), + }, }, }, } @@ -170,7 +189,7 @@ func (t *LaunchTemplate) RenderCloudformation(target *cloudformation.Cloudformat } } for _, x := range e.SecurityGroups { - data.VpcSecurityGroupIDs = append(data.VpcSecurityGroupIDs, x.CloudformationLink()) + data.NetworkInterfaces[0].SecurityGroups = append(data.NetworkInterfaces[0].SecurityGroups, x.CloudformationLink()) } if e.SSHKey != nil { data.KeyName = e.SSHKey.Name @@ -233,5 +252,17 @@ func (t *LaunchTemplate) RenderCloudformation(target *cloudformation.Cloudformat }) } + if e.Tags != nil { + tags := buildCloudformationTags(t.Tags) + data.TagSpecifications = append(data.TagSpecifications, &cloudformationLaunchTemplateTagSpecification{ + ResourceType: fi.String("instance"), + Tags: tags, + }) + data.TagSpecifications = append(data.TagSpecifications, &cloudformationLaunchTemplateTagSpecification{ + ResourceType: fi.String("volume"), + Tags: tags, + }) + } + return target.RenderResource("AWS::EC2::LaunchTemplate", fi.StringValue(e.Name), cf) } diff --git a/upup/pkg/fi/cloudup/awstasks/launchtemplate_target_cloudformation_test.go b/upup/pkg/fi/cloudup/awstasks/launchtemplate_target_cloudformation_test.go index b4bddb6cf81b4..e905965931b4e 100644 --- a/upup/pkg/fi/cloudup/awstasks/launchtemplate_target_cloudformation_test.go +++ b/upup/pkg/fi/cloudup/awstasks/launchtemplate_target_cloudformation_test.go @@ -64,21 +64,22 @@ func TestLaunchTemplateCloudformationRender(t *testing.T) { "NetworkInterfaces": [ { "AssociatePublicIpAddress": true, - "DeleteOnTermination": true + "DeleteOnTermination": true, + "DeviceIndex": 0, + "Groups": [ + { + "Ref": "AWSEC2SecurityGroupnodes1" + }, + { + "Ref": "AWSEC2SecurityGroupnodes2" + } + ] } ], "Placement": [ { "Tenancy": "dedicated" } - ], - "SecurityGroup": [ - { - "Ref": "AWSEC2SecurityGroupnodes1" - }, - { - "Ref": "AWSEC2SecurityGroupnodes2" - } ] } } @@ -127,7 +128,7 @@ func TestLaunchTemplateCloudformationRender(t *testing.T) { "BlockDeviceMappings": [ { "DeviceName": "/dev/xvdd", - "EBS": { + "Ebs": { "VolumeType": "gp2", "VolumeSize": 100, "DeleteOnTermination": true, @@ -146,21 +147,22 @@ func TestLaunchTemplateCloudformationRender(t *testing.T) { "NetworkInterfaces": [ { "AssociatePublicIpAddress": true, - "DeleteOnTermination": true + "DeleteOnTermination": true, + "DeviceIndex": 0, + "Groups": [ + { + "Ref": "AWSEC2SecurityGroupnodes1" + }, + { + "Ref": "AWSEC2SecurityGroupnodes2" + } + ] } ], "Placement": [ { "Tenancy": "dedicated" } - ], - "SecurityGroup": [ - { - "Ref": "AWSEC2SecurityGroupnodes1" - }, - { - "Ref": "AWSEC2SecurityGroupnodes2" - } ] } } diff --git a/upup/pkg/fi/cloudup/awstasks/launchtemplate_target_terraform.go b/upup/pkg/fi/cloudup/awstasks/launchtemplate_target_terraform.go index 0d8b3f2cf9681..1aaa58c6b6b14 100644 --- a/upup/pkg/fi/cloudup/awstasks/launchtemplate_target_terraform.go +++ b/upup/pkg/fi/cloudup/awstasks/launchtemplate_target_terraform.go @@ -24,12 +24,12 @@ import ( "k8s.io/kops/upup/pkg/fi/cloudup/terraform" ) -type terraformLaunchTemplateNetworkInterfaces struct { +type terraformLaunchTemplateNetworkInterface struct { // AssociatePublicIPAddress associates a public ip address with the network interface. Boolean value. AssociatePublicIPAddress *bool `json:"associate_public_ip_address,omitempty"` // DeleteOnTermination indicates whether the network interface should be destroyed on instance termination. DeleteOnTermination *bool `json:"delete_on_termination,omitempty"` - // SecurityGroups is a list of security group ids + // SecurityGroups is a list of security group ids. SecurityGroups []*terraform.Literal `json:"security_groups,omitempty"` } @@ -100,6 +100,13 @@ type terraformLaunchTemplateBlockDevice struct { EBS []*terraformLaunchTemplateBlockDeviceEBS `json:"ebs,omitempty"` } +type terraformLaunchTemplateTagSpecification struct { + // The type of resource to tag + ResourceType *string `json:"resource_type,omitempty"` + // The tags to apply to the resource. + Tags map[string]string `json:"tags,omitempty"` +} + type terraformLaunchTemplate struct { // NamePrefix is the name of the launch template NamePrefix *string `json:"name_prefix,omitempty"` @@ -123,9 +130,11 @@ type terraformLaunchTemplate struct { // Monitoring are the instance monitoring options Monitoring []*terraformLaunchTemplateMonitoring `json:"monitoring,omitempty"` // NetworkInterfaces are the networking options - NetworkInterfaces []*terraformLaunchTemplateNetworkInterfaces `json:"network_interfaces,omitempty"` + NetworkInterfaces []*terraformLaunchTemplateNetworkInterface `json:"network_interfaces,omitempty"` // Placement are the tenancy options Placement []*terraformLaunchTemplatePlacement `json:"placement,omitempty"` + // TagSpecifications specifies tags to apply to a resource when the resource is created + TagSpecifications []*terraformLaunchTemplateTagSpecification `json:"tag_specifications,omitempty"` // UserData is the user data for the instances UserData *terraform.Literal `json:"user_data,omitempty"` } @@ -161,9 +170,11 @@ func (t *LaunchTemplate) RenderTerraform(target *terraform.TerraformTarget, a, e ImageID: image, InstanceType: e.InstanceType, Lifecycle: &terraform.Lifecycle{CreateBeforeDestroy: fi.Bool(true)}, - NetworkInterfaces: []*terraformLaunchTemplateNetworkInterfaces{ - {AssociatePublicIPAddress: e.AssociatePublicIP, - DeleteOnTermination: fi.Bool(true)}, + NetworkInterfaces: []*terraformLaunchTemplateNetworkInterface{ + { + AssociatePublicIPAddress: e.AssociatePublicIP, + DeleteOnTermination: fi.Bool(true), + }, }, } @@ -249,5 +260,16 @@ func (t *LaunchTemplate) RenderTerraform(target *terraform.TerraformTarget, a, e }) } + if e.Tags != nil { + tf.TagSpecifications = append(tf.TagSpecifications, &terraformLaunchTemplateTagSpecification{ + ResourceType: fi.String("instance"), + Tags: e.Tags, + }) + tf.TagSpecifications = append(tf.TagSpecifications, &terraformLaunchTemplateTagSpecification{ + ResourceType: fi.String("volume"), + Tags: e.Tags, + }) + } + return target.RenderResource("aws_launch_template", fi.StringValue(e.Name), tf) } From 576e546b5e6b6d538b5242138e62f252d75d0c7e Mon Sep 17 00:00:00 2001 From: Ciprian Hacman Date: Mon, 16 Mar 2020 18:01:04 +0200 Subject: [PATCH 2/3] Update integration tests for launch templates --- .../launch_templates/cloudformation.json | 304 ++++++++++++++++-- .../launch_templates/kubernetes.tf | 128 +++++++- 2 files changed, 404 insertions(+), 28 deletions(-) diff --git a/tests/integration/update_cluster/launch_templates/cloudformation.json b/tests/integration/update_cluster/launch_templates/cloudformation.json index d7ea119377818..315c8a5596e0f 100644 --- a/tests/integration/update_cluster/launch_templates/cloudformation.json +++ b/tests/integration/update_cluster/launch_templates/cloudformation.json @@ -4,6 +4,17 @@ "Type": "AWS::AutoScaling::AutoScalingGroup", "Properties": { "AutoScalingGroupName": "master-us-test-1a.masters.launchtemplates.example.com", + "LaunchTemplate": { + "LaunchTemplateId": { + "Ref": "AWSEC2LaunchTemplatemasterustest1amasterslaunchtemplatesexamplecom" + }, + "Version": { + "Fn::GetAtt": [ + "AWSEC2LaunchTemplatemasterustest1amasterslaunchtemplatesexamplecom", + "LatestVersionNumber" + ] + } + }, "MaxSize": 1, "MinSize": 1, "VPCZoneIdentifier": [ @@ -59,6 +70,17 @@ "Type": "AWS::AutoScaling::AutoScalingGroup", "Properties": { "AutoScalingGroupName": "master-us-test-1b.masters.launchtemplates.example.com", + "LaunchTemplate": { + "LaunchTemplateId": { + "Ref": "AWSEC2LaunchTemplatemasterustest1bmasterslaunchtemplatesexamplecom" + }, + "Version": { + "Fn::GetAtt": [ + "AWSEC2LaunchTemplatemasterustest1bmasterslaunchtemplatesexamplecom", + "LatestVersionNumber" + ] + } + }, "MaxSize": 1, "MinSize": 1, "VPCZoneIdentifier": [ @@ -114,6 +136,17 @@ "Type": "AWS::AutoScaling::AutoScalingGroup", "Properties": { "AutoScalingGroupName": "master-us-test-1c.masters.launchtemplates.example.com", + "LaunchTemplate": { + "LaunchTemplateId": { + "Ref": "AWSEC2LaunchTemplatemasterustest1cmasterslaunchtemplatesexamplecom" + }, + "Version": { + "Fn::GetAtt": [ + "AWSEC2LaunchTemplatemasterustest1cmasterslaunchtemplatesexamplecom", + "LatestVersionNumber" + ] + } + }, "MaxSize": 1, "MinSize": 1, "VPCZoneIdentifier": [ @@ -169,6 +202,17 @@ "Type": "AWS::AutoScaling::AutoScalingGroup", "Properties": { "AutoScalingGroupName": "nodes.launchtemplates.example.com", + "LaunchTemplate": { + "LaunchTemplateId": { + "Ref": "AWSEC2LaunchTemplatenodeslaunchtemplatesexamplecom" + }, + "Version": { + "Fn::GetAtt": [ + "AWSEC2LaunchTemplatenodeslaunchtemplatesexamplecom", + "LatestVersionNumber" + ] + } + }, "MaxSize": 2, "MinSize": 2, "VPCZoneIdentifier": [ @@ -270,7 +314,7 @@ "BlockDeviceMappings": [ { "DeviceName": "/dev/xvda", - "EBS": { + "Ebs": { "VolumeType": "gp2", "VolumeSize": 64, "DeleteOnTermination": true @@ -288,15 +332,68 @@ "NetworkInterfaces": [ { "AssociatePublicIpAddress": true, - "DeleteOnTermination": true + "DeleteOnTermination": true, + "DeviceIndex": 0, + "Groups": [ + { + "Ref": "AWSEC2SecurityGroupmasterslaunchtemplatesexamplecom" + } + ] } ], - "UserData": "extracted", - "SecurityGroup": [ + "TagSpecifications": [ { - "Ref": "AWSEC2SecurityGroupmasterslaunchtemplatesexamplecom" + "ResourceType": "instance", + "Tags": [ + { + "Key": "KubernetesCluster", + "Value": "launchtemplates.example.com" + }, + { + "Key": "Name", + "Value": "master-us-test-1a.masters.launchtemplates.example.com" + }, + { + "Key": "k8s.io/role/master", + "Value": "1" + }, + { + "Key": "kops.k8s.io/instancegroup", + "Value": "master-us-test-1a" + }, + { + "Key": "kubernetes.io/cluster/launchtemplates.example.com", + "Value": "owned" + } + ] + }, + { + "ResourceType": "volume", + "Tags": [ + { + "Key": "KubernetesCluster", + "Value": "launchtemplates.example.com" + }, + { + "Key": "Name", + "Value": "master-us-test-1a.masters.launchtemplates.example.com" + }, + { + "Key": "k8s.io/role/master", + "Value": "1" + }, + { + "Key": "kops.k8s.io/instancegroup", + "Value": "master-us-test-1a" + }, + { + "Key": "kubernetes.io/cluster/launchtemplates.example.com", + "Value": "owned" + } + ] } - ] + ], + "UserData": "extracted" } } }, @@ -308,7 +405,7 @@ "BlockDeviceMappings": [ { "DeviceName": "/dev/xvda", - "EBS": { + "Ebs": { "VolumeType": "gp2", "VolumeSize": 64, "DeleteOnTermination": true @@ -326,15 +423,68 @@ "NetworkInterfaces": [ { "AssociatePublicIpAddress": true, - "DeleteOnTermination": true + "DeleteOnTermination": true, + "DeviceIndex": 0, + "Groups": [ + { + "Ref": "AWSEC2SecurityGroupmasterslaunchtemplatesexamplecom" + } + ] } ], - "UserData": "extracted", - "SecurityGroup": [ + "TagSpecifications": [ { - "Ref": "AWSEC2SecurityGroupmasterslaunchtemplatesexamplecom" + "ResourceType": "instance", + "Tags": [ + { + "Key": "KubernetesCluster", + "Value": "launchtemplates.example.com" + }, + { + "Key": "Name", + "Value": "master-us-test-1b.masters.launchtemplates.example.com" + }, + { + "Key": "k8s.io/role/master", + "Value": "1" + }, + { + "Key": "kops.k8s.io/instancegroup", + "Value": "master-us-test-1b" + }, + { + "Key": "kubernetes.io/cluster/launchtemplates.example.com", + "Value": "owned" + } + ] + }, + { + "ResourceType": "volume", + "Tags": [ + { + "Key": "KubernetesCluster", + "Value": "launchtemplates.example.com" + }, + { + "Key": "Name", + "Value": "master-us-test-1b.masters.launchtemplates.example.com" + }, + { + "Key": "k8s.io/role/master", + "Value": "1" + }, + { + "Key": "kops.k8s.io/instancegroup", + "Value": "master-us-test-1b" + }, + { + "Key": "kubernetes.io/cluster/launchtemplates.example.com", + "Value": "owned" + } + ] } - ] + ], + "UserData": "extracted" } } }, @@ -346,7 +496,7 @@ "BlockDeviceMappings": [ { "DeviceName": "/dev/xvda", - "EBS": { + "Ebs": { "VolumeType": "gp2", "VolumeSize": 64, "DeleteOnTermination": true @@ -364,15 +514,68 @@ "NetworkInterfaces": [ { "AssociatePublicIpAddress": true, - "DeleteOnTermination": true + "DeleteOnTermination": true, + "DeviceIndex": 0, + "Groups": [ + { + "Ref": "AWSEC2SecurityGroupmasterslaunchtemplatesexamplecom" + } + ] } ], - "UserData": "extracted", - "SecurityGroup": [ + "TagSpecifications": [ + { + "ResourceType": "instance", + "Tags": [ + { + "Key": "KubernetesCluster", + "Value": "launchtemplates.example.com" + }, + { + "Key": "Name", + "Value": "master-us-test-1c.masters.launchtemplates.example.com" + }, + { + "Key": "k8s.io/role/master", + "Value": "1" + }, + { + "Key": "kops.k8s.io/instancegroup", + "Value": "master-us-test-1c" + }, + { + "Key": "kubernetes.io/cluster/launchtemplates.example.com", + "Value": "owned" + } + ] + }, { - "Ref": "AWSEC2SecurityGroupmasterslaunchtemplatesexamplecom" + "ResourceType": "volume", + "Tags": [ + { + "Key": "KubernetesCluster", + "Value": "launchtemplates.example.com" + }, + { + "Key": "Name", + "Value": "master-us-test-1c.masters.launchtemplates.example.com" + }, + { + "Key": "k8s.io/role/master", + "Value": "1" + }, + { + "Key": "kops.k8s.io/instancegroup", + "Value": "master-us-test-1c" + }, + { + "Key": "kubernetes.io/cluster/launchtemplates.example.com", + "Value": "owned" + } + ] } - ] + ], + "UserData": "extracted" } } }, @@ -384,7 +587,7 @@ "BlockDeviceMappings": [ { "DeviceName": "/dev/xvda", - "EBS": { + "Ebs": { "VolumeType": "gp2", "VolumeSize": 128, "DeleteOnTermination": true @@ -402,15 +605,68 @@ "NetworkInterfaces": [ { "AssociatePublicIpAddress": true, - "DeleteOnTermination": true + "DeleteOnTermination": true, + "DeviceIndex": 0, + "Groups": [ + { + "Ref": "AWSEC2SecurityGroupnodeslaunchtemplatesexamplecom" + } + ] } ], - "UserData": "extracted", - "SecurityGroup": [ + "TagSpecifications": [ { - "Ref": "AWSEC2SecurityGroupnodeslaunchtemplatesexamplecom" + "ResourceType": "instance", + "Tags": [ + { + "Key": "KubernetesCluster", + "Value": "launchtemplates.example.com" + }, + { + "Key": "Name", + "Value": "nodes.launchtemplates.example.com" + }, + { + "Key": "k8s.io/role/node", + "Value": "1" + }, + { + "Key": "kops.k8s.io/instancegroup", + "Value": "nodes" + }, + { + "Key": "kubernetes.io/cluster/launchtemplates.example.com", + "Value": "owned" + } + ] + }, + { + "ResourceType": "volume", + "Tags": [ + { + "Key": "KubernetesCluster", + "Value": "launchtemplates.example.com" + }, + { + "Key": "Name", + "Value": "nodes.launchtemplates.example.com" + }, + { + "Key": "k8s.io/role/node", + "Value": "1" + }, + { + "Key": "kops.k8s.io/instancegroup", + "Value": "nodes" + }, + { + "Key": "kubernetes.io/cluster/launchtemplates.example.com", + "Value": "owned" + } + ] } - ] + ], + "UserData": "extracted" } } }, diff --git a/tests/integration/update_cluster/launch_templates/kubernetes.tf b/tests/integration/update_cluster/launch_templates/kubernetes.tf index 2ebeb6d161f51..e46f6be77f5f1 100644 --- a/tests/integration/update_cluster/launch_templates/kubernetes.tf +++ b/tests/integration/update_cluster/launch_templates/kubernetes.tf @@ -91,7 +91,13 @@ provider "aws" { } resource "aws_autoscaling_group" "master-us-test-1a-masters-launchtemplates-example-com" { - name = "master-us-test-1a.masters.launchtemplates.example.com" + name = "master-us-test-1a.masters.launchtemplates.example.com" + + launch_template = { + id = "${aws_launch_template.master-us-test-1a-masters-launchtemplates-example-com.id}" + version = "${aws_launch_template.master-us-test-1a-masters-launchtemplates-example-com.latest_version}" + } + max_size = 1 min_size = 1 vpc_zone_identifier = ["${aws_subnet.us-test-1a-launchtemplates-example-com.id}"] @@ -131,7 +137,13 @@ resource "aws_autoscaling_group" "master-us-test-1a-masters-launchtemplates-exam } resource "aws_autoscaling_group" "master-us-test-1b-masters-launchtemplates-example-com" { - name = "master-us-test-1b.masters.launchtemplates.example.com" + name = "master-us-test-1b.masters.launchtemplates.example.com" + + launch_template = { + id = "${aws_launch_template.master-us-test-1b-masters-launchtemplates-example-com.id}" + version = "${aws_launch_template.master-us-test-1b-masters-launchtemplates-example-com.latest_version}" + } + max_size = 1 min_size = 1 vpc_zone_identifier = ["${aws_subnet.us-test-1b-launchtemplates-example-com.id}"] @@ -171,7 +183,13 @@ resource "aws_autoscaling_group" "master-us-test-1b-masters-launchtemplates-exam } resource "aws_autoscaling_group" "master-us-test-1c-masters-launchtemplates-example-com" { - name = "master-us-test-1c.masters.launchtemplates.example.com" + name = "master-us-test-1c.masters.launchtemplates.example.com" + + launch_template = { + id = "${aws_launch_template.master-us-test-1c-masters-launchtemplates-example-com.id}" + version = "${aws_launch_template.master-us-test-1c-masters-launchtemplates-example-com.latest_version}" + } + max_size = 1 min_size = 1 vpc_zone_identifier = ["${aws_subnet.us-test-1c-launchtemplates-example-com.id}"] @@ -211,7 +229,13 @@ resource "aws_autoscaling_group" "master-us-test-1c-masters-launchtemplates-exam } resource "aws_autoscaling_group" "nodes-launchtemplates-example-com" { - name = "nodes.launchtemplates.example.com" + name = "nodes.launchtemplates.example.com" + + launch_template = { + id = "${aws_launch_template.nodes-launchtemplates-example-com.id}" + version = "${aws_launch_template.nodes-launchtemplates-example-com.latest_version}" + } + max_size = 2 min_size = 2 vpc_zone_identifier = ["${aws_subnet.us-test-1b-launchtemplates-example-com.id}"] @@ -419,6 +443,30 @@ resource "aws_launch_template" "master-us-test-1a-masters-launchtemplates-exampl security_groups = ["${aws_security_group.masters-launchtemplates-example-com.id}"] } + tag_specifications = { + resource_type = "instance" + + tags = { + KubernetesCluster = "launchtemplates.example.com" + Name = "master-us-test-1a.masters.launchtemplates.example.com" + "k8s.io/role/master" = "1" + "kops.k8s.io/instancegroup" = "master-us-test-1a" + "kubernetes.io/cluster/launchtemplates.example.com" = "owned" + } + } + + tag_specifications = { + resource_type = "volume" + + tags = { + KubernetesCluster = "launchtemplates.example.com" + Name = "master-us-test-1a.masters.launchtemplates.example.com" + "k8s.io/role/master" = "1" + "kops.k8s.io/instancegroup" = "master-us-test-1a" + "kubernetes.io/cluster/launchtemplates.example.com" = "owned" + } + } + user_data = "${file("${path.module}/data/aws_launch_template_master-us-test-1a.masters.launchtemplates.example.com_user_data")}" } @@ -453,6 +501,30 @@ resource "aws_launch_template" "master-us-test-1b-masters-launchtemplates-exampl security_groups = ["${aws_security_group.masters-launchtemplates-example-com.id}"] } + tag_specifications = { + resource_type = "instance" + + tags = { + KubernetesCluster = "launchtemplates.example.com" + Name = "master-us-test-1b.masters.launchtemplates.example.com" + "k8s.io/role/master" = "1" + "kops.k8s.io/instancegroup" = "master-us-test-1b" + "kubernetes.io/cluster/launchtemplates.example.com" = "owned" + } + } + + tag_specifications = { + resource_type = "volume" + + tags = { + KubernetesCluster = "launchtemplates.example.com" + Name = "master-us-test-1b.masters.launchtemplates.example.com" + "k8s.io/role/master" = "1" + "kops.k8s.io/instancegroup" = "master-us-test-1b" + "kubernetes.io/cluster/launchtemplates.example.com" = "owned" + } + } + user_data = "${file("${path.module}/data/aws_launch_template_master-us-test-1b.masters.launchtemplates.example.com_user_data")}" } @@ -487,6 +559,30 @@ resource "aws_launch_template" "master-us-test-1c-masters-launchtemplates-exampl security_groups = ["${aws_security_group.masters-launchtemplates-example-com.id}"] } + tag_specifications = { + resource_type = "instance" + + tags = { + KubernetesCluster = "launchtemplates.example.com" + Name = "master-us-test-1c.masters.launchtemplates.example.com" + "k8s.io/role/master" = "1" + "kops.k8s.io/instancegroup" = "master-us-test-1c" + "kubernetes.io/cluster/launchtemplates.example.com" = "owned" + } + } + + tag_specifications = { + resource_type = "volume" + + tags = { + KubernetesCluster = "launchtemplates.example.com" + Name = "master-us-test-1c.masters.launchtemplates.example.com" + "k8s.io/role/master" = "1" + "kops.k8s.io/instancegroup" = "master-us-test-1c" + "kubernetes.io/cluster/launchtemplates.example.com" = "owned" + } + } + user_data = "${file("${path.module}/data/aws_launch_template_master-us-test-1c.masters.launchtemplates.example.com_user_data")}" } @@ -521,6 +617,30 @@ resource "aws_launch_template" "nodes-launchtemplates-example-com" { security_groups = ["${aws_security_group.nodes-launchtemplates-example-com.id}"] } + tag_specifications = { + resource_type = "instance" + + tags = { + KubernetesCluster = "launchtemplates.example.com" + Name = "nodes.launchtemplates.example.com" + "k8s.io/role/node" = "1" + "kops.k8s.io/instancegroup" = "nodes" + "kubernetes.io/cluster/launchtemplates.example.com" = "owned" + } + } + + tag_specifications = { + resource_type = "volume" + + tags = { + KubernetesCluster = "launchtemplates.example.com" + Name = "nodes.launchtemplates.example.com" + "k8s.io/role/node" = "1" + "kops.k8s.io/instancegroup" = "nodes" + "kubernetes.io/cluster/launchtemplates.example.com" = "owned" + } + } + user_data = "${file("${path.module}/data/aws_launch_template_nodes.launchtemplates.example.com_user_data")}" } From fb68aae41fde7c5ae1ab344b27c1ecb2e163e0b8 Mon Sep 17 00:00:00 2001 From: Ciprian Hacman Date: Mon, 16 Mar 2020 19:48:19 +0200 Subject: [PATCH 3/3] Apply suggestions from code review #1 Co-Authored-By: John Gardiner Myers --- upup/pkg/fi/cloudup/awstasks/autoscalinggroup.go | 16 ++++++++-------- .../launchtemplate_target_cloudformation.go | 8 ++++---- .../awstasks/launchtemplate_target_terraform.go | 6 +++--- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/upup/pkg/fi/cloudup/awstasks/autoscalinggroup.go b/upup/pkg/fi/cloudup/awstasks/autoscalinggroup.go index a0f138e721596..bca7a50b6d549 100644 --- a/upup/pkg/fi/cloudup/awstasks/autoscalinggroup.go +++ b/upup/pkg/fi/cloudup/awstasks/autoscalinggroup.go @@ -263,7 +263,7 @@ func (v *AutoscalingGroup) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *Autos VPCZoneIdentifier: fi.String(strings.Join(e.AutoscalingGroupSubnets(), ",")), } - // @check are we using a launch configuation or mixed instance policies or launch template + // @check are we using a launch configuration, mixed instances policy, or launch template if e.LaunchConfiguration != nil { request.LaunchConfigurationName = e.LaunchConfiguration.ID } else if e.UseMixedInstancesPolicy() { @@ -294,7 +294,7 @@ func (v *AutoscalingGroup) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *Autos Version: aws.String("1"), } } else { - return fmt.Errorf("could not find on of launch configuation, mixed instance policies or launch template") + return fmt.Errorf("could not find one of launch configuration, mixed instances policy, or launch template") } // @step: attempt to create the autoscaling group for us @@ -611,9 +611,9 @@ type terraformASGTag struct { } type terraformAutoscalingLaunchTemplateSpecification struct { - // LaunchTemplateID is the ID of the template to use + // LaunchTemplateID is the ID of the template to use. LaunchTemplateID *terraform.Literal `json:"id,omitempty"` - // Version is the version of the Launch Template to use + // Version is the version of the Launch Template to use. Version *terraform.Literal `json:"version,omitempty"` } @@ -739,7 +739,7 @@ func (_ *AutoscalingGroup) RenderTerraform(t *terraform.TerraformTarget, a, e, c Version: e.LaunchTemplate.VersionLink(), } } else { - return fmt.Errorf("could not find on of launch configuation, mixed instance policies or launch template") + return fmt.Errorf("could not find one of launch configuration, mixed instances policy, or launch template") } role := "" @@ -811,9 +811,9 @@ type cloudformationASGMetricsCollection struct { } type cloudformationAutoscalingLaunchTemplateSpecification struct { - // LaunchTemplateId is the IDx of the template to use + // LaunchTemplateId is the IDx of the template to use. LaunchTemplateId *cloudformation.Literal `json:"LaunchTemplateId,omitempty"` - // Version is the version number of the template to use + // Version is the version number of the template to use. Version *cloudformation.Literal `json:"Version,omitempty"` } @@ -908,7 +908,7 @@ func (_ *AutoscalingGroup) RenderCloudformation(t *cloudformation.Cloudformation Version: e.LaunchTemplate.CloudformationVersion(), } } else { - return fmt.Errorf("could not find on of launch configuation, mixed instance policies or launch template") + return fmt.Errorf("could not find one of launch configuration, mixed instances policy, or launch template") } for _, s := range e.Subnets { diff --git a/upup/pkg/fi/cloudup/awstasks/launchtemplate_target_cloudformation.go b/upup/pkg/fi/cloudup/awstasks/launchtemplate_target_cloudformation.go index 9f20e59f6ef0e..45d4a9c287750 100644 --- a/upup/pkg/fi/cloudup/awstasks/launchtemplate_target_cloudformation.go +++ b/upup/pkg/fi/cloudup/awstasks/launchtemplate_target_cloudformation.go @@ -100,9 +100,9 @@ type cloudformationLaunchTemplateBlockDevice struct { } type cloudformationLaunchTemplateTagSpecification struct { - // The type of resource to tag + // ResourceType is the type of resource to tag. ResourceType *string `json:"ResourceType,omitempty"` - // The tags to apply to the resource. + // Tags are the tags to apply to the resource. Tags []cloudformationTag `json:"Tags,omitempty"` } @@ -127,7 +127,7 @@ type cloudformationLaunchTemplateData struct { NetworkInterfaces []*cloudformationLaunchTemplateNetworkInterface `json:"NetworkInterfaces,omitempty"` // Placement are the tenancy options Placement []*cloudformationLaunchTemplatePlacement `json:"Placement,omitempty"` - // TagSpecifications specifies tags to apply to a resource when the resource is created + // TagSpecifications are the tags to apply to a resource when it is created. TagSpecifications []*cloudformationLaunchTemplateTagSpecification `json:"TagSpecifications,omitempty"` // UserData is the user data for the instances UserData *string `json:"UserData,omitempty"` @@ -145,7 +145,7 @@ func (t *LaunchTemplate) CloudformationLink() *cloudformation.Literal { return cloudformation.Ref("AWS::EC2::LaunchTemplate", fi.StringValue(t.Name)) } -// CloudformationLink returns the cloudformation version for us +// CloudformationLink returns the cloudformation version. func (t *LaunchTemplate) CloudformationVersion() *cloudformation.Literal { return cloudformation.GetAtt("AWS::EC2::LaunchTemplate", fi.StringValue(t.Name), "LatestVersionNumber") } diff --git a/upup/pkg/fi/cloudup/awstasks/launchtemplate_target_terraform.go b/upup/pkg/fi/cloudup/awstasks/launchtemplate_target_terraform.go index 1aaa58c6b6b14..e12fdd7d0c249 100644 --- a/upup/pkg/fi/cloudup/awstasks/launchtemplate_target_terraform.go +++ b/upup/pkg/fi/cloudup/awstasks/launchtemplate_target_terraform.go @@ -101,9 +101,9 @@ type terraformLaunchTemplateBlockDevice struct { } type terraformLaunchTemplateTagSpecification struct { - // The type of resource to tag + // ResourceType is the type of resource to tag. ResourceType *string `json:"resource_type,omitempty"` - // The tags to apply to the resource. + // Tags are the tags to apply to the resource. Tags map[string]string `json:"tags,omitempty"` } @@ -133,7 +133,7 @@ type terraformLaunchTemplate struct { NetworkInterfaces []*terraformLaunchTemplateNetworkInterface `json:"network_interfaces,omitempty"` // Placement are the tenancy options Placement []*terraformLaunchTemplatePlacement `json:"placement,omitempty"` - // TagSpecifications specifies tags to apply to a resource when the resource is created + // TagSpecifications are the tags to apply to a resource when it is created. TagSpecifications []*terraformLaunchTemplateTagSpecification `json:"tag_specifications,omitempty"` // UserData is the user data for the instances UserData *terraform.Literal `json:"user_data,omitempty"`