From 33e6465084c9de9e21a8b796b35479567a6ecc96 Mon Sep 17 00:00:00 2001 From: M00nF1sh Date: Tue, 28 May 2019 19:09:24 -0700 Subject: [PATCH] add accessLogs support for aws nlb --- .../aws/aws_loadbalancer.go | 156 +++++++++++------- 1 file changed, 100 insertions(+), 56 deletions(-) diff --git a/staging/src/k8s.io/legacy-cloud-providers/aws/aws_loadbalancer.go b/staging/src/k8s.io/legacy-cloud-providers/aws/aws_loadbalancer.go index 3d6b50840d..34f41659ae 100644 --- a/staging/src/k8s.io/legacy-cloud-providers/aws/aws_loadbalancer.go +++ b/staging/src/k8s.io/legacy-cloud-providers/aws/aws_loadbalancer.go @@ -45,6 +45,11 @@ const ( // SSLNegotiationPolicyNameFormat is a format string used for the SSL // negotiation policy tag name SSLNegotiationPolicyNameFormat = "k8s-SSLNegotiationPolicy-%s" + + lbAttrLoadBalancingCrossZoneEnabled = "load_balancing.cross_zone.enabled" + lbAttrAccessLogsS3Enabled = "access_logs.s3.enabled" + lbAttrAccessLogsS3Bucket = "access_logs.s3.bucket" + lbAttrAccessLogsS3Prefix = "access_logs.s3.prefix" ) var ( @@ -319,62 +324,8 @@ func (c *Cloud) ensureLoadBalancerv2(namespacedName types.NamespacedName, loadBa } } - desiredLoadBalancerAttributes := map[string]string{} - // Default values to ensured a remove annotation reverts back to the default - desiredLoadBalancerAttributes["load_balancing.cross_zone.enabled"] = "false" - - // Determine if cross zone load balancing enabled/disabled has been specified - crossZoneLoadBalancingEnabledAnnotation := annotations[ServiceAnnotationLoadBalancerCrossZoneLoadBalancingEnabled] - if crossZoneLoadBalancingEnabledAnnotation != "" { - crossZoneEnabled, err := strconv.ParseBool(crossZoneLoadBalancingEnabledAnnotation) - if err != nil { - return nil, fmt.Errorf("error parsing service annotation: %s=%s", - ServiceAnnotationLoadBalancerCrossZoneLoadBalancingEnabled, - crossZoneLoadBalancingEnabledAnnotation, - ) - } - - if crossZoneEnabled { - desiredLoadBalancerAttributes["load_balancing.cross_zone.enabled"] = "true" - } - } - - // Whether the ELB was new or existing, sync attributes regardless. This accounts for things - // that cannot be specified at the time of creation and can only be modified after the fact, - // e.g. idle connection timeout. - describeAttributesRequest := &elbv2.DescribeLoadBalancerAttributesInput{ - LoadBalancerArn: loadBalancer.LoadBalancerArn, - } - describeAttributesOutput, err := c.elbv2.DescribeLoadBalancerAttributes(describeAttributesRequest) - if err != nil { - return nil, fmt.Errorf("Unable to retrieve load balancer attributes during attribute sync: %q", err) - } - - changedAttributes := []*elbv2.LoadBalancerAttribute{} - - // Identify to be changed attributes - for _, foundAttribute := range describeAttributesOutput.Attributes { - if targetValue, ok := desiredLoadBalancerAttributes[*foundAttribute.Key]; ok { - if targetValue != *foundAttribute.Value { - changedAttributes = append(changedAttributes, &elbv2.LoadBalancerAttribute{ - Key: foundAttribute.Key, - Value: aws.String(targetValue), - }) - } - } - } - - // Update attributes requiring changes - if len(changedAttributes) > 0 { - klog.V(2).Infof("Updating load-balancer attributes for %q", loadBalancerName) - - _, err = c.elbv2.ModifyLoadBalancerAttributes(&elbv2.ModifyLoadBalancerAttributesInput{ - LoadBalancerArn: loadBalancer.LoadBalancerArn, - Attributes: changedAttributes, - }) - if err != nil { - return nil, fmt.Errorf("Unable to update load balancer attributes during attribute sync: %q", err) - } + if err := c.reconcileLBAttributes(aws.StringValue(loadBalancer.LoadBalancerArn), annotations); err != nil { + return nil, err } // Subnets cannot be modified on NLBs @@ -395,6 +346,99 @@ func (c *Cloud) ensureLoadBalancerv2(namespacedName types.NamespacedName, loadBa return loadBalancer, nil } +func (c *Cloud) reconcileLBAttributes(loadBalancerArn string, annotations map[string]string) error { + desiredLoadBalancerAttributes := map[string]string{} + + desiredLoadBalancerAttributes[lbAttrLoadBalancingCrossZoneEnabled] = "false" + crossZoneLoadBalancingEnabledAnnotation := annotations[ServiceAnnotationLoadBalancerCrossZoneLoadBalancingEnabled] + if crossZoneLoadBalancingEnabledAnnotation != "" { + crossZoneEnabled, err := strconv.ParseBool(crossZoneLoadBalancingEnabledAnnotation) + if err != nil { + return fmt.Errorf("error parsing service annotation: %s=%s", + ServiceAnnotationLoadBalancerCrossZoneLoadBalancingEnabled, + crossZoneLoadBalancingEnabledAnnotation, + ) + } + + if crossZoneEnabled { + desiredLoadBalancerAttributes[lbAttrLoadBalancingCrossZoneEnabled] = "true" + } + } + + desiredLoadBalancerAttributes[lbAttrAccessLogsS3Enabled] = "false" + accessLogsS3EnabledAnnotation := annotations[ServiceAnnotationLoadBalancerAccessLogEnabled] + if accessLogsS3EnabledAnnotation != "" { + accessLogsS3Enabled, err := strconv.ParseBool(accessLogsS3EnabledAnnotation) + if err != nil { + return fmt.Errorf("error parsing service annotation: %s=%s", + ServiceAnnotationLoadBalancerAccessLogEnabled, + accessLogsS3EnabledAnnotation, + ) + } + + if accessLogsS3Enabled { + desiredLoadBalancerAttributes[lbAttrAccessLogsS3Enabled] = "true" + } + } + + desiredLoadBalancerAttributes[lbAttrAccessLogsS3Bucket] = annotations[ServiceAnnotationLoadBalancerAccessLogS3BucketName] + desiredLoadBalancerAttributes[lbAttrAccessLogsS3Prefix] = annotations[ServiceAnnotationLoadBalancerAccessLogS3BucketPrefix] + + currentLoadBalancerAttributes := map[string]string{} + describeAttributesOutput, err := c.elbv2.DescribeLoadBalancerAttributes(&elbv2.DescribeLoadBalancerAttributesInput{ + LoadBalancerArn: aws.String(loadBalancerArn), + }) + if err != nil { + return fmt.Errorf("unable to retrieve load balancer attributes during attribute sync: %q", err) + } + for _, attr := range describeAttributesOutput.Attributes { + currentLoadBalancerAttributes[aws.StringValue(attr.Key)] = aws.StringValue(attr.Value) + } + + var changedAttributes []*elbv2.LoadBalancerAttribute + if desiredLoadBalancerAttributes[lbAttrLoadBalancingCrossZoneEnabled] != currentLoadBalancerAttributes[lbAttrLoadBalancingCrossZoneEnabled] { + changedAttributes = append(changedAttributes, &elbv2.LoadBalancerAttribute{ + Key: aws.String(lbAttrLoadBalancingCrossZoneEnabled), + Value: aws.String(desiredLoadBalancerAttributes[lbAttrLoadBalancingCrossZoneEnabled]), + }) + } + if desiredLoadBalancerAttributes[lbAttrAccessLogsS3Enabled] != currentLoadBalancerAttributes[lbAttrAccessLogsS3Enabled] { + changedAttributes = append(changedAttributes, &elbv2.LoadBalancerAttribute{ + Key: aws.String(lbAttrAccessLogsS3Enabled), + Value: aws.String(desiredLoadBalancerAttributes[lbAttrAccessLogsS3Enabled]), + }) + } + + // ELBV2 API forbids us to set bucket to an empty bucket, so we keep it unchanged if AccessLogsS3Enabled==false. + if desiredLoadBalancerAttributes[lbAttrAccessLogsS3Enabled] == "true" { + if desiredLoadBalancerAttributes[lbAttrAccessLogsS3Bucket] != currentLoadBalancerAttributes[lbAttrAccessLogsS3Bucket] { + changedAttributes = append(changedAttributes, &elbv2.LoadBalancerAttribute{ + Key: aws.String(lbAttrAccessLogsS3Bucket), + Value: aws.String(desiredLoadBalancerAttributes[lbAttrAccessLogsS3Bucket]), + }) + } + if desiredLoadBalancerAttributes[lbAttrAccessLogsS3Prefix] != currentLoadBalancerAttributes[lbAttrAccessLogsS3Prefix] { + changedAttributes = append(changedAttributes, &elbv2.LoadBalancerAttribute{ + Key: aws.String(lbAttrAccessLogsS3Prefix), + Value: aws.String(desiredLoadBalancerAttributes[lbAttrAccessLogsS3Prefix]), + }) + } + } + + if len(changedAttributes) > 0 { + klog.V(2).Infof("updating load-balancer attributes for %q", loadBalancerArn) + + _, err = c.elbv2.ModifyLoadBalancerAttributes(&elbv2.ModifyLoadBalancerAttributesInput{ + LoadBalancerArn: aws.String(loadBalancerArn), + Attributes: changedAttributes, + }) + if err != nil { + return fmt.Errorf("unable to update load balancer attributes during attribute sync: %q", err) + } + } + return nil +} + var invalidELBV2NameRegex = regexp.MustCompile("[^[:alnum:]]") // buildTargetGroupName will build unique name for targetGroup of service & port.