diff --git a/k8s/crds/kops.k8s.io_clusters.yaml b/k8s/crds/kops.k8s.io_clusters.yaml index 3d6edc0808..3d3e35960a 100644 --- a/k8s/crds/kops.k8s.io_clusters.yaml +++ b/k8s/crds/kops.k8s.io_clusters.yaml @@ -81,6 +81,22 @@ spec: description: LoadBalancer is the configuration for the kube-apiserver ELB properties: + accessLog: + description: AccessLog is the configuration of access logs + properties: + bucket: + description: Bucket is S3 bucket name to store the logs + in + type: string + bucketPrefix: + description: BucketPrefix is S3 bucket prefix. Logs are + stored in the root if not configured. + type: string + interval: + description: Interval is publishing interval in minutes. + This parameter is only used with classic load balancer. + type: integer + type: object additionalSecurityGroups: description: AdditionalSecurityGroups attaches additional security groups (e.g. sg-123456). diff --git a/pkg/apis/kops/cluster.go b/pkg/apis/kops/cluster.go index 06bf6cf1f4..e7c5537831 100644 --- a/pkg/apis/kops/cluster.go +++ b/pkg/apis/kops/cluster.go @@ -414,6 +414,15 @@ const ( LoadBalancerClassNetwork LoadBalancerClass = "Network" ) +type AccessLogSpec struct { + // Interval is the publishing interval in minutes. This parameter is only used with classic load balancer. + Interval int `json:"interval,omitempty"` + // Bucket is the S3 bucket name to store the logs in. + Bucket string `json:"bucket,omitempty"` + // BucketPrefix is the S3 bucket prefix. Logs are stored in the root if not configured. + BucketPrefix string `json:"bucketPrefix,omitempty"` +} + var SupportedLoadBalancerClasses = []string{ string(LoadBalancerClassClassic), string(LoadBalancerClassNetwork), @@ -451,6 +460,8 @@ type LoadBalancerAccessSpec struct { CrossZoneLoadBalancing *bool `json:"crossZoneLoadBalancing,omitempty"` // Subnets allows you to specify the subnets that must be used for the load balancer Subnets []LoadBalancerSubnetSpec `json:"subnets,omitempty"` + // AccessLog is the configuration of access logs. + AccessLog *AccessLogSpec `json:"accessLog,omitempty"` } // KubeDNSConfig defines the kube dns configuration diff --git a/pkg/apis/kops/v1alpha2/cluster.go b/pkg/apis/kops/v1alpha2/cluster.go index 279533270e..73ec2aab63 100644 --- a/pkg/apis/kops/v1alpha2/cluster.go +++ b/pkg/apis/kops/v1alpha2/cluster.go @@ -415,6 +415,15 @@ const ( LoadBalancerClassNetwork LoadBalancerClass = "Network" ) +type AccessLogSpec struct { + // Interval is publishing interval in minutes. This parameter is only used with classic load balancer. + Interval int `json:"interval,omitempty"` + // Bucket is S3 bucket name to store the logs in + Bucket string `json:"bucket,omitempty"` + // BucketPrefix is S3 bucket prefix. Logs are stored in the root if not configured. + BucketPrefix string `json:"bucketPrefix,omitempty"` +} + var SupportedLoadBalancerClasses = []string{ string(LoadBalancerClassClassic), string(LoadBalancerClassNetwork), @@ -452,6 +461,8 @@ type LoadBalancerAccessSpec struct { CrossZoneLoadBalancing *bool `json:"crossZoneLoadBalancing,omitempty"` // Subnets allows you to specify the subnets that must be used for the load balancer Subnets []LoadBalancerSubnetSpec `json:"subnets,omitempty"` + // AccessLog is the configuration of access logs + AccessLog *AccessLogSpec `json:"accessLog,omitempty"` } // KubeDNSConfig defines the kube dns configuration diff --git a/pkg/apis/kops/v1alpha2/zz_generated.conversion.go b/pkg/apis/kops/v1alpha2/zz_generated.conversion.go index 1fad333e04..ac39e9a18d 100644 --- a/pkg/apis/kops/v1alpha2/zz_generated.conversion.go +++ b/pkg/apis/kops/v1alpha2/zz_generated.conversion.go @@ -63,6 +63,16 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddGeneratedConversionFunc((*AccessLogSpec)(nil), (*kops.AccessLogSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_AccessLogSpec_To_kops_AccessLogSpec(a.(*AccessLogSpec), b.(*kops.AccessLogSpec), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*kops.AccessLogSpec)(nil), (*AccessLogSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_kops_AccessLogSpec_To_v1alpha2_AccessLogSpec(a.(*kops.AccessLogSpec), b.(*AccessLogSpec), scope) + }); err != nil { + return err + } if err := s.AddGeneratedConversionFunc((*AccessSpec)(nil), (*kops.AccessSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1alpha2_AccessSpec_To_kops_AccessSpec(a.(*AccessSpec), b.(*kops.AccessSpec), scope) }); err != nil { @@ -1194,6 +1204,30 @@ func Convert_kops_AWSPermission_To_v1alpha2_AWSPermission(in *kops.AWSPermission return autoConvert_kops_AWSPermission_To_v1alpha2_AWSPermission(in, out, s) } +func autoConvert_v1alpha2_AccessLogSpec_To_kops_AccessLogSpec(in *AccessLogSpec, out *kops.AccessLogSpec, s conversion.Scope) error { + out.Interval = in.Interval + out.Bucket = in.Bucket + out.BucketPrefix = in.BucketPrefix + return nil +} + +// Convert_v1alpha2_AccessLogSpec_To_kops_AccessLogSpec is an autogenerated conversion function. +func Convert_v1alpha2_AccessLogSpec_To_kops_AccessLogSpec(in *AccessLogSpec, out *kops.AccessLogSpec, s conversion.Scope) error { + return autoConvert_v1alpha2_AccessLogSpec_To_kops_AccessLogSpec(in, out, s) +} + +func autoConvert_kops_AccessLogSpec_To_v1alpha2_AccessLogSpec(in *kops.AccessLogSpec, out *AccessLogSpec, s conversion.Scope) error { + out.Interval = in.Interval + out.Bucket = in.Bucket + out.BucketPrefix = in.BucketPrefix + return nil +} + +// Convert_kops_AccessLogSpec_To_v1alpha2_AccessLogSpec is an autogenerated conversion function. +func Convert_kops_AccessLogSpec_To_v1alpha2_AccessLogSpec(in *kops.AccessLogSpec, out *AccessLogSpec, s conversion.Scope) error { + return autoConvert_kops_AccessLogSpec_To_v1alpha2_AccessLogSpec(in, out, s) +} + func autoConvert_v1alpha2_AccessSpec_To_kops_AccessSpec(in *AccessSpec, out *kops.AccessSpec, s conversion.Scope) error { if in.DNS != nil { in, out := &in.DNS, &out.DNS @@ -5457,6 +5491,15 @@ func autoConvert_v1alpha2_LoadBalancerAccessSpec_To_kops_LoadBalancerAccessSpec( } else { out.Subnets = nil } + if in.AccessLog != nil { + in, out := &in.AccessLog, &out.AccessLog + *out = new(kops.AccessLogSpec) + if err := Convert_v1alpha2_AccessLogSpec_To_kops_AccessLogSpec(*in, *out, s); err != nil { + return err + } + } else { + out.AccessLog = nil + } return nil } @@ -5486,6 +5529,15 @@ func autoConvert_kops_LoadBalancerAccessSpec_To_v1alpha2_LoadBalancerAccessSpec( } else { out.Subnets = nil } + if in.AccessLog != nil { + in, out := &in.AccessLog, &out.AccessLog + *out = new(AccessLogSpec) + if err := Convert_kops_AccessLogSpec_To_v1alpha2_AccessLogSpec(*in, *out, s); err != nil { + return err + } + } else { + out.AccessLog = nil + } return nil } diff --git a/pkg/apis/kops/v1alpha2/zz_generated.deepcopy.go b/pkg/apis/kops/v1alpha2/zz_generated.deepcopy.go index 32c881c051..63e0530cbe 100644 --- a/pkg/apis/kops/v1alpha2/zz_generated.deepcopy.go +++ b/pkg/apis/kops/v1alpha2/zz_generated.deepcopy.go @@ -104,6 +104,22 @@ func (in *AWSPermission) DeepCopy() *AWSPermission { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AccessLogSpec) DeepCopyInto(out *AccessLogSpec) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AccessLogSpec. +func (in *AccessLogSpec) DeepCopy() *AccessLogSpec { + if in == nil { + return nil + } + out := new(AccessLogSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *AccessSpec) DeepCopyInto(out *AccessSpec) { *out = *in @@ -3649,6 +3665,11 @@ func (in *LoadBalancerAccessSpec) DeepCopyInto(out *LoadBalancerAccessSpec) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.AccessLog != nil { + in, out := &in.AccessLog, &out.AccessLog + *out = new(AccessLogSpec) + **out = **in + } return } diff --git a/pkg/apis/kops/zz_generated.deepcopy.go b/pkg/apis/kops/zz_generated.deepcopy.go index 827ca162b4..40ba0f8ede 100644 --- a/pkg/apis/kops/zz_generated.deepcopy.go +++ b/pkg/apis/kops/zz_generated.deepcopy.go @@ -104,6 +104,22 @@ func (in *AWSPermission) DeepCopy() *AWSPermission { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AccessLogSpec) DeepCopyInto(out *AccessLogSpec) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AccessLogSpec. +func (in *AccessLogSpec) DeepCopy() *AccessLogSpec { + if in == nil { + return nil + } + out := new(AccessLogSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *AccessSpec) DeepCopyInto(out *AccessSpec) { *out = *in @@ -3831,6 +3847,11 @@ func (in *LoadBalancerAccessSpec) DeepCopyInto(out *LoadBalancerAccessSpec) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.AccessLog != nil { + in, out := &in.AccessLog, &out.AccessLog + *out = new(AccessLogSpec) + **out = **in + } return } diff --git a/pkg/model/awsmodel/api_loadbalancer.go b/pkg/model/awsmodel/api_loadbalancer.go index 42fbd7e5db..808ee3bcfd 100644 --- a/pkg/model/awsmodel/api_loadbalancer.go +++ b/pkg/model/awsmodel/api_loadbalancer.go @@ -237,6 +237,27 @@ func (b *APILoadBalancerBuilder) Build(c *fi.ModelBuilderContext) error { return fmt.Errorf("unknown load balancer Type: %q", lbSpec.Type) } + if lbSpec.AccessLog != nil { + clb.AccessLog = &awstasks.ClassicLoadBalancerAccessLog{ + EmitInterval: fi.Int64(int64(lbSpec.AccessLog.Interval)), + Enabled: fi.Bool(true), + S3BucketName: fi.String(lbSpec.AccessLog.Bucket), + S3BucketPrefix: fi.String(lbSpec.AccessLog.BucketPrefix), + } + nlb.AccessLog = &awstasks.NetworkLoadBalancerAccessLog{ + Enabled: fi.Bool(true), + S3BucketName: fi.String(lbSpec.AccessLog.Bucket), + S3BucketPrefix: fi.String(lbSpec.AccessLog.BucketPrefix), + } + } else { + clb.AccessLog = &awstasks.ClassicLoadBalancerAccessLog{ + Enabled: fi.Bool(false), + } + nlb.AccessLog = &awstasks.NetworkLoadBalancerAccessLog{ + Enabled: fi.Bool(false), + } + } + if b.APILoadBalancerClass() == kops.LoadBalancerClassClassic { c.AddTask(clb) } else if b.APILoadBalancerClass() == kops.LoadBalancerClassNetwork { diff --git a/tests/integration/update_cluster/complex/cloudformation.json b/tests/integration/update_cluster/complex/cloudformation.json index 678d11d8ce..4b5516e0b9 100644 --- a/tests/integration/update_cluster/complex/cloudformation.json +++ b/tests/integration/update_cluster/complex/cloudformation.json @@ -1458,6 +1458,20 @@ "Key": "kubernetes.io/cluster/complex.example.com", "Value": "owned" } + ], + "LoadBalancerAttributes": [ + { + "Key": "access_logs.s3.enabled", + "Value": "true" + }, + { + "Key": "access_logs.s3.bucket", + "Value": "access-log-example" + }, + { + "Key": "access_logs.s3.prefix", + "Value": "" + } ] } }, diff --git a/tests/integration/update_cluster/complex/data/aws_s3_bucket_object_cluster-completed.spec_content b/tests/integration/update_cluster/complex/data/aws_s3_bucket_object_cluster-completed.spec_content index 908c21ac0e..44951703e9 100644 --- a/tests/integration/update_cluster/complex/data/aws_s3_bucket_object_cluster-completed.spec_content +++ b/tests/integration/update_cluster/complex/data/aws_s3_bucket_object_cluster-completed.spec_content @@ -9,6 +9,8 @@ spec: - 10.2.0.0/16 api: loadBalancer: + accessLog: + bucket: access-log-example additionalSecurityGroups: - sg-exampleid5 - sg-exampleid6 diff --git a/tests/integration/update_cluster/complex/in-legacy-v1alpha2.yaml b/tests/integration/update_cluster/complex/in-legacy-v1alpha2.yaml index 49240f4da0..1d48c29daf 100644 --- a/tests/integration/update_cluster/complex/in-legacy-v1alpha2.yaml +++ b/tests/integration/update_cluster/complex/in-legacy-v1alpha2.yaml @@ -17,6 +17,8 @@ spec: subnets: - name: us-test-1a allocationId: eipalloc-012345a678b9cdefa + accessLog: + bucket: access-log-example kubernetesApiAccess: - 1.1.1.0/24 channel: stable diff --git a/tests/integration/update_cluster/complex/in-v1alpha2.yaml b/tests/integration/update_cluster/complex/in-v1alpha2.yaml index 882f42d99f..ca5b723431 100644 --- a/tests/integration/update_cluster/complex/in-v1alpha2.yaml +++ b/tests/integration/update_cluster/complex/in-v1alpha2.yaml @@ -17,6 +17,8 @@ spec: subnets: - name: us-test-1a allocationId: eipalloc-012345a678b9cdefa + accessLog: + bucket: access-log-example kubernetesApiAccess: - 1.1.1.0/24 channel: stable diff --git a/tests/integration/update_cluster/complex/kubernetes.tf b/tests/integration/update_cluster/complex/kubernetes.tf index dc51cce5db..9f08f2141f 100644 --- a/tests/integration/update_cluster/complex/kubernetes.tf +++ b/tests/integration/update_cluster/complex/kubernetes.tf @@ -523,6 +523,11 @@ resource "aws_launch_template" "nodes-complex-example-com" { } resource "aws_lb" "api-complex-example-com" { + access_logs { + bucket = "access-log-example" + enabled = true + prefix = "" + } enable_cross_zone_load_balancing = true internal = false load_balancer_type = "network" diff --git a/upup/pkg/fi/cloudup/awstasks/classic_load_balancer.go b/upup/pkg/fi/cloudup/awstasks/classic_load_balancer.go index 4f2ee6fccf..08941599ac 100644 --- a/upup/pkg/fi/cloudup/awstasks/classic_load_balancer.go +++ b/upup/pkg/fi/cloudup/awstasks/classic_load_balancer.go @@ -716,7 +716,7 @@ func (_ *ClassicLoadBalancer) RenderTerraform(t *terraform.TerraformTarget, a, e } } - if e.AccessLog != nil { + if e.AccessLog != nil && fi.BoolValue(e.AccessLog.Enabled) { tf.AccessLog = &terraformLoadBalancerAccessLog{ EmitInterval: e.AccessLog.EmitInterval, Enabled: e.AccessLog.Enabled, @@ -856,7 +856,7 @@ func (_ *ClassicLoadBalancer) RenderCloudformation(t *cloudformation.Cloudformat } } - if e.AccessLog != nil { + if e.AccessLog != nil && fi.BoolValue(e.AccessLog.Enabled) { tf.AccessLog = &cloudformationClassicLoadBalancerAccessLog{ EmitInterval: e.AccessLog.EmitInterval, Enabled: e.AccessLog.Enabled, diff --git a/upup/pkg/fi/cloudup/awstasks/classic_loadbalancer_attributes.go b/upup/pkg/fi/cloudup/awstasks/classic_loadbalancer_attributes.go index cb893b6f51..2c1ec749c9 100644 --- a/upup/pkg/fi/cloudup/awstasks/classic_loadbalancer_attributes.go +++ b/upup/pkg/fi/cloudup/awstasks/classic_loadbalancer_attributes.go @@ -153,6 +153,9 @@ func (_ *ClassicLoadBalancer) modifyLoadBalancerAttributes(t *awsup.AWSAPITarget // request.LoadBalancerAttributes.AdditionalAttributes = additionalAttributes //} + if e.AccessLog != nil && e.AccessLog.Enabled != nil { + request.LoadBalancerAttributes.AccessLog.Enabled = e.AccessLog.Enabled + } if e.AccessLog != nil && e.AccessLog.EmitInterval != nil { request.LoadBalancerAttributes.AccessLog.EmitInterval = e.AccessLog.EmitInterval } diff --git a/upup/pkg/fi/cloudup/awstasks/network_load_balancer.go b/upup/pkg/fi/cloudup/awstasks/network_load_balancer.go index fdfbc2ffc7..5058a225f1 100644 --- a/upup/pkg/fi/cloudup/awstasks/network_load_balancer.go +++ b/upup/pkg/fi/cloudup/awstasks/network_load_balancer.go @@ -69,6 +69,7 @@ type NetworkLoadBalancer struct { VPC *VPC TargetGroups []*TargetGroup + AccessLog *NetworkLoadBalancerAccessLog } var _ fi.CompareWithID = &NetworkLoadBalancer{} @@ -365,6 +366,25 @@ func (e *NetworkLoadBalancer) Find(c *fi.Context) (*NetworkLoadBalancer, error) return nil, err } actual.CrossZoneLoadBalancing = fi.Bool(b) + case "access_logs.s3.enabled": + b, err := strconv.ParseBool(*value) + if err != nil { + return nil, err + } + if actual.AccessLog == nil { + actual.AccessLog = &NetworkLoadBalancerAccessLog{} + } + actual.AccessLog.Enabled = fi.Bool(b) + case "access_logs.s3.bucket": + if actual.AccessLog == nil { + actual.AccessLog = &NetworkLoadBalancerAccessLog{} + } + actual.AccessLog.S3BucketName = value + case "access_logs.s3.prefix": + if actual.AccessLog == nil { + actual.AccessLog = &NetworkLoadBalancerAccessLog{} + } + actual.AccessLog.S3BucketPrefix = value default: klog.V(2).Infof("unsupported key -- ignoring, %v.\n", key) } @@ -454,6 +474,17 @@ func (s *NetworkLoadBalancer) CheckChanges(a, e, changes *NetworkLoadBalancer) e return fi.RequiredField("CrossZoneLoadBalancing") } } + + if e.AccessLog != nil { + if e.AccessLog.Enabled == nil { + return fi.RequiredField("Accesslog.Enabled") + } + if *e.AccessLog.Enabled { + if e.AccessLog.S3BucketName == nil { + return fi.RequiredField("Accesslog.S3Bucket") + } + } + } } else { if len(changes.SubnetMappings) > 0 { expectedSubnets := make(map[string]*string) @@ -666,6 +697,7 @@ type terraformNetworkLoadBalancer struct { Type string `json:"load_balancer_type" cty:"load_balancer_type"` SubnetMappings []terraformNetworkLoadBalancerSubnetMapping `json:"subnet_mapping" cty:"subnet_mapping"` CrossZoneLoadBalancing bool `json:"enable_cross_zone_load_balancing" cty:"enable_cross_zone_load_balancing"` + AccessLog *terraformNetworkLoadBalancerAccessLog `json:"access_logs,omitempty" cty:"access_logs"` Tags map[string]string `json:"tags" cty:"tags"` } @@ -707,6 +739,14 @@ func (_ *NetworkLoadBalancer) RenderTerraform(t *terraform.TerraformTarget, a, e }) } + if e.AccessLog != nil && fi.BoolValue(e.AccessLog.Enabled) { + nlbTF.AccessLog = &terraformNetworkLoadBalancerAccessLog{ + Enabled: e.AccessLog.Enabled, + S3BucketName: e.AccessLog.S3BucketName, + S3BucketPrefix: e.AccessLog.S3BucketPrefix, + } + } + err := t.RenderResource("aws_lb", *e.Name, nlbTF) if err != nil { return err @@ -748,6 +788,7 @@ func (_ *NetworkLoadBalancer) RenderTerraform(t *terraform.TerraformTarget, a, e return err } } + return nil } @@ -760,11 +801,12 @@ func (e *NetworkLoadBalancer) TerraformLink(params ...string) *terraformWriter.L } type cloudformationNetworkLoadBalancer struct { - Name string `json:"Name"` - Scheme string `json:"Scheme"` - SubnetMappings []*cloudformationSubnetMapping `json:"SubnetMappings"` - Type string `json:"Type"` - Tags []cloudformationTag `json:"Tags"` + Name string `json:"Name"` + Scheme string `json:"Scheme"` + SubnetMappings []*cloudformationSubnetMapping `json:"SubnetMappings"` + Type string `json:"Type"` + Tags []cloudformationTag `json:"Tags"` + LoadBalancerAttributes []cloudformationLoadBalancerAttribute `json:"LoadBalancerAttributes,omitempty"` } type cloudformationSubnetMapping struct { @@ -773,6 +815,11 @@ type cloudformationSubnetMapping struct { PrivateIPv4Address *string `json:"PrivateIPv4Address,omitempty"` } +type cloudformationLoadBalancerAttribute struct { + Key *string `json:"Key"` + Value *string `json:"Value,omitempty"` +} + type cloudformationNetworkLoadBalancerListener struct { Certificates []cloudformationNetworkLoadBalancerListenerCertificate `json:"Certificates,omitempty"` DefaultActions []cloudformationNetworkLoadBalancerListenerAction `json:"DefaultActions"` @@ -809,6 +856,25 @@ func (_ *NetworkLoadBalancer) RenderCloudformation(t *cloudformation.Cloudformat } else { nlbCF.Scheme = elbv2.LoadBalancerSchemeEnumInternetFacing } + + if e.AccessLog != nil && *e.AccessLog.Enabled { + var attributes []cloudformationLoadBalancerAttribute + + attributes = append(attributes, cloudformationLoadBalancerAttribute{ + Key: aws.String("access_logs.s3.enabled"), + Value: aws.String(strconv.FormatBool(aws.BoolValue(e.AccessLog.Enabled))), + }) + attributes = append(attributes, cloudformationLoadBalancerAttribute{ + Key: aws.String("access_logs.s3.bucket"), + Value: e.AccessLog.S3BucketName, + }) + attributes = append(attributes, cloudformationLoadBalancerAttribute{ + Key: aws.String("access_logs.s3.prefix"), + Value: e.AccessLog.S3BucketPrefix, + }) + nlbCF.LoadBalancerAttributes = attributes + } + err := t.RenderResource("AWS::ElasticLoadBalancingV2::LoadBalancer", *e.Name, nlbCF) if err != nil { return err diff --git a/upup/pkg/fi/cloudup/awstasks/networkloadbalancer_attributes.go b/upup/pkg/fi/cloudup/awstasks/networkloadbalancer_attributes.go index 761c76d8b6..460f25f930 100644 --- a/upup/pkg/fi/cloudup/awstasks/networkloadbalancer_attributes.go +++ b/upup/pkg/fi/cloudup/awstasks/networkloadbalancer_attributes.go @@ -27,6 +27,22 @@ import ( "k8s.io/kops/upup/pkg/fi/cloudup/awsup" ) +type NetworkLoadBalancerAccessLog struct { + Enabled *bool + S3BucketName *string + S3BucketPrefix *string +} + +func (_ *NetworkLoadBalancerAccessLog) GetDependencies(tasks map[string]fi.Task) []fi.Task { + return nil +} + +type terraformNetworkLoadBalancerAccessLog struct { + Enabled *bool `json:"enabled,omitempty" cty:"enabled"` + S3BucketName *string `json:"bucket,omitempty" cty:"bucket"` + S3BucketPrefix *string `json:"bucket_prefix,omitempty" cty:"prefix"` +} + func findNetworkLoadBalancerAttributes(cloud awsup.AWSCloud, LoadBalancerArn string) ([]*elbv2.LoadBalancerAttribute, error) { request := &elbv2.DescribeLoadBalancerAttributesInput{ @@ -52,7 +68,7 @@ func findNetworkLoadBalancerAttributes(cloud awsup.AWSCloud, LoadBalancerArn str } func (_ *NetworkLoadBalancer) modifyLoadBalancerAttributes(t *awsup.AWSAPITarget, a, e, changes *NetworkLoadBalancer, loadBalancerArn string) error { - if changes.CrossZoneLoadBalancing == nil { + if changes.CrossZoneLoadBalancing == nil && changes.AccessLog == nil { klog.V(4).Infof("No LoadBalancerAttribute changes; skipping update") return nil } @@ -74,6 +90,28 @@ func (_ *NetworkLoadBalancer) modifyLoadBalancerAttributes(t *awsup.AWSAPITarget } attributes = append(attributes, attribute) + if e.AccessLog != nil { + attr := &elbv2.LoadBalancerAttribute{ + Key: aws.String("access_logs.s3.enabled"), + Value: aws.String(strconv.FormatBool(aws.BoolValue(e.AccessLog.Enabled))), + } + attributes = append(attributes, attr) + } + if e.AccessLog != nil && e.AccessLog.S3BucketName != nil { + attr := &elbv2.LoadBalancerAttribute{ + Key: aws.String("access_logs.s3.bucket"), + Value: e.AccessLog.S3BucketName, + } + attributes = append(attributes, attr) + } + if e.AccessLog != nil && e.AccessLog.S3BucketPrefix != nil { + attr := &elbv2.LoadBalancerAttribute{ + Key: aws.String("access_logs.s3.prefix"), + Value: e.AccessLog.S3BucketPrefix, + } + attributes = append(attributes, attr) + } + request.Attributes = attributes klog.V(2).Infof("Configuring NLB attributes for NLB %q", loadBalancerName)