Skip to content

Commit

Permalink
Update the clouddriver lambda APIs to support additional attributes a…
Browse files Browse the repository at this point in the history
…nd Update the load balancer to support lambda target. (#4169)

* feat(aws) : Add lambda support and add lambda target to load balancer.

* feat(provider/aws): Add tags, environment variables to update lambda configuration.

* feat(provider/aws): Add tags, environment variables to update lambda configuration.

* feat(aws) : Add lambda support and add lambda target to load balancer.

feat(provider/aws): Add tags, environment variables to update lambda configuration.

feat(provider/aws): Add tags, environment variables to update lambda configuration.

* feat(provider/aws): Implemented the comments in, #4169

* feat(provider/aws): Implemented the comments in, #4169.
  • Loading branch information
madhusarma authored and mergify[bot] committed Dec 5, 2019
1 parent 2bb89ec commit 81d60a7
Show file tree
Hide file tree
Showing 17 changed files with 640 additions and 70 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package com.netflix.spinnaker.clouddriver.aws.data

import com.amazonaws.services.elasticloadbalancingv2.model.TargetTypeEnum
import com.google.common.collect.ImmutableMap
import com.google.common.collect.ImmutableSet
import com.netflix.frigga.Names
Expand Down Expand Up @@ -175,7 +176,12 @@ class Keys implements KeyParser {
}

static String getTargetGroupKey(String targetGroupName, String account, String region, String targetGroupType, String vpcId) {
"${ID}:${Namespace.TARGET_GROUPS}:${account}:${region}:${targetGroupName}:${targetGroupType}:${vpcId}"
//Lambda targetGroup don't have the vpcId
if (TargetTypeEnum.Lambda.toString().equalsIgnoreCase(targetGroupType)) {
"${ID}:${Namespace.TARGET_GROUPS}:${account}:${region}:${targetGroupName}:${targetGroupType}"
} else {
"${ID}:${Namespace.TARGET_GROUPS}:${account}:${region}:${targetGroupName}:${targetGroupType}:${vpcId}"
}
}

static String getClusterKey(String clusterName, String application, String account) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,8 @@ public static class Attributes {
private String stickinessType = "lb_cookie";
private Integer stickinessDuration = 86400;
private Boolean proxyProtocolV2 = false;
/** The following attribute is supported only if the target is a Lambda function. */
private Boolean multiValueHeadersEnabled = false;

public Integer getDeregistrationDelay() {
return deregistrationDelay;
Expand Down Expand Up @@ -374,6 +376,14 @@ public Boolean getProxyProtocolV2() {
public void setProxyProtocolV2(Boolean proxyProtocolV2) {
this.proxyProtocolV2 = proxyProtocolV2;
}

public Boolean getMultiValueHeadersEnabled() {
return multiValueHeadersEnabled;
}

public void setMultiValueHeadersEnabled(Boolean multiValueHeadersEnabled) {
this.multiValueHeadersEnabled = multiValueHeadersEnabled;
}
}

public static class RuleCondition {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,24 +42,31 @@ class LoadBalancerV2UpsertHandler {
private static String modifyTargetGroupAttributes(AmazonElasticLoadBalancing loadBalancing, LoadBalancer loadBalancer, TargetGroup targetGroup, UpsertAmazonLoadBalancerV2Description.Attributes attributes, DeployDefaults deployDefaults) {
def targetGroupAttributes = []
if (attributes) {
Integer deregistrationDelay = [attributes.deregistrationDelay, deployDefaults?.loadBalancing?.deregistrationDelayDefault].findResult(Closure.IDENTITY)
if (deregistrationDelay != null) {
targetGroupAttributes.add(new TargetGroupAttribute(key: "deregistration_delay.timeout_seconds", value: deregistrationDelay.toString()))
}
if (loadBalancer.type == 'application') {
if (attributes.stickinessEnabled != null) {
targetGroupAttributes.add(new TargetGroupAttribute(key: "stickiness.enabled", value: attributes.stickinessEnabled.toString()))
if (TargetTypeEnum.Lambda.toString().equalsIgnoreCase(targetGroup.getTargetType()))
{
if (attributes.multiValueHeadersEnabled != null) {
targetGroupAttributes.add(new TargetGroupAttribute(key: "lambda.multi_value_headers.enabled", value: attributes.multiValueHeadersEnabled))
}
if (attributes.stickinessType != null) {
targetGroupAttributes.add(new TargetGroupAttribute(key: "stickiness.type", value: attributes.stickinessType))
} else {
Integer deregistrationDelay = [attributes.deregistrationDelay, deployDefaults?.loadBalancing?.deregistrationDelayDefault].findResult(Closure.IDENTITY)
if (deregistrationDelay != null) {
targetGroupAttributes.add(new TargetGroupAttribute(key: "deregistration_delay.timeout_seconds", value: deregistrationDelay.toString()))
}
if (attributes.stickinessDuration != null) {
targetGroupAttributes.add(new TargetGroupAttribute(key: "stickiness.lb_cookie.duration_seconds", value: attributes.stickinessDuration.toString()))
if (loadBalancer.type == 'application') {
if (attributes.stickinessEnabled != null) {
targetGroupAttributes.add(new TargetGroupAttribute(key: "stickiness.enabled", value: attributes.stickinessEnabled.toString()))
}
if (attributes.stickinessType != null) {
targetGroupAttributes.add(new TargetGroupAttribute(key: "stickiness.type", value: attributes.stickinessType))
}
if (attributes.stickinessDuration != null) {
targetGroupAttributes.add(new TargetGroupAttribute(key: "stickiness.lb_cookie.duration_seconds", value: attributes.stickinessDuration.toString()))
}
}
}
if(loadBalancer.type == 'network' ){
if(attributes.proxyProtocolV2 != null){
targetGroupAttributes.add(new TargetGroupAttribute(key: "proxy_protocol_v2.enabled", value: attributes.proxyProtocolV2))
if (loadBalancer.type == 'network' ) {
if(attributes.proxyProtocolV2 != null) {
targetGroupAttributes.add(new TargetGroupAttribute(key: "proxy_protocol_v2.enabled", value: attributes.proxyProtocolV2))
}
}
}
}
Expand All @@ -84,9 +91,23 @@ class LoadBalancerV2UpsertHandler {
targetGroupsToCreate.each { targetGroup ->
TargetGroup createdTargetGroup
try {
String status = "Target group created in ${loadBalancerName} (${targetGroup.name}:${targetGroup.port}:${targetGroup.protocol})."
CreateTargetGroupRequest createTargetGroupRequest = new CreateTargetGroupRequest();
if (TargetTypeEnum.Lambda.toString().equalsIgnoreCase(targetGroup.targetType)) {

CreateTargetGroupRequest createTargetGroupRequest = new CreateTargetGroupRequest()
.withProtocol(targetGroup.protocol)
createTargetGroupRequest.withName(targetGroup.name)
.withHealthCheckIntervalSeconds(targetGroup.healthCheckInterval)
.withHealthCheckTimeoutSeconds(targetGroup.healthCheckTimeout)
.withHealthyThresholdCount(targetGroup.healthyThreshold)
.withUnhealthyThresholdCount(targetGroup.unhealthyThreshold)
.withTargetType(targetGroup.targetType)
.withMatcher(new Matcher().withHttpCode(targetGroup.healthCheckMatcher))
.withHealthCheckPath(targetGroup.healthCheckPath)

status = "Lambda Target group created in ${loadBalancerName} (${targetGroup.name})."

} else {
createTargetGroupRequest.withProtocol(targetGroup.protocol)
.withPort(targetGroup.port)
.withName(targetGroup.name)
.withVpcId(loadBalancer.vpcId)
Expand All @@ -97,22 +118,23 @@ class LoadBalancerV2UpsertHandler {
.withUnhealthyThresholdCount(targetGroup.unhealthyThreshold)
.withTargetType(targetGroup.targetType)

if (targetGroup.healthCheckProtocol in [ProtocolEnum.HTTP, ProtocolEnum.HTTPS]) {
createTargetGroupRequest
.withHealthCheckPath(targetGroup.healthCheckPath)

// HTTP(s) health checks for TCP does not support custom matchers and timeouts. Also, health thresholds must be equal.
if (targetGroup.protocol == ProtocolEnum.TCP) {
createTargetGroupRequest.withUnhealthyThresholdCount(createTargetGroupRequest.getHealthyThresholdCount())
} else {
createTargetGroupRequest.withMatcher(new Matcher().withHttpCode(targetGroup.healthCheckMatcher))
.withHealthCheckTimeoutSeconds(targetGroup.healthCheckTimeout)
if (targetGroup.healthCheckProtocol in [ProtocolEnum.HTTP, ProtocolEnum.HTTPS]) {
createTargetGroupRequest
.withHealthCheckPath(targetGroup.healthCheckPath)

// HTTP(s) health checks for TCP does not support custom matchers and timeouts. Also, health thresholds must be equal.
if (targetGroup.protocol == ProtocolEnum.TCP) {
createTargetGroupRequest.withUnhealthyThresholdCount(createTargetGroupRequest.getHealthyThresholdCount())
} else {
createTargetGroupRequest.withMatcher(new Matcher().withHttpCode(targetGroup.healthCheckMatcher))
.withHealthCheckTimeoutSeconds(targetGroup.healthCheckTimeout)
}
}
}

CreateTargetGroupResult createTargetGroupResult = loadBalancing.createTargetGroup( createTargetGroupRequest )
task.updateStatus BASE_PHASE, "Target group created in ${loadBalancerName} (${targetGroup.name}:${targetGroup.port}:${targetGroup.protocol})."
task.updateStatus BASE_PHASE, status
createdTargetGroup = createTargetGroupResult.getTargetGroups().get(0)

} catch (AmazonServiceException e) {
String exceptionMessage = "Failed to create target group ${targetGroup.name} for ${loadBalancerName} - reason: ${e.errorMessage}."
task.updateStatus BASE_PHASE, exceptionMessage
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@
package com.netflix.spinnaker.clouddriver.aws.deploy.validators;

import com.amazonaws.services.elasticloadbalancingv2.model.AuthenticateOidcActionConfig;
import com.amazonaws.services.elasticloadbalancingv2.model.TargetTypeEnum;
import com.netflix.spinnaker.clouddriver.aws.AmazonOperation;
import com.netflix.spinnaker.clouddriver.aws.deploy.description.UpsertAmazonLoadBalancerClassicDescription;
import com.netflix.spinnaker.clouddriver.aws.deploy.description.UpsertAmazonLoadBalancerDescription;
import com.netflix.spinnaker.clouddriver.aws.deploy.description.UpsertAmazonLoadBalancerV2Description;
import com.netflix.spinnaker.clouddriver.aws.model.AmazonLoadBalancerType;
import com.netflix.spinnaker.clouddriver.aws.security.AmazonCredentials;
import com.netflix.spinnaker.clouddriver.orchestration.AtomicOperations;
import java.util.HashSet;
Expand Down Expand Up @@ -137,14 +139,18 @@ public void validate(
errors.rejectValue(
"targetGroups", "createAmazonLoadBalancerDescription.targetGroups.name.missing");
}
if (targetGroup.getProtocol() == null) {
errors.rejectValue(
"targetGroups",
"createAmazonLoadBalancerDescription.targetGroups.protocol.missing");
}
if (targetGroup.getPort() == null) {
errors.rejectValue(
"targetGroups", "createAmazonLoadBalancerDescription.targetGroups.port.missing");
if (TargetTypeEnum.Lambda.toString().equalsIgnoreCase(targetGroup.getTargetType())) {
validateLambdaTargetGroup(albDescription, targetGroup, errors);
} else {
if (targetGroup.getProtocol() == null) {
errors.rejectValue(
"targetGroups",
"createAmazonLoadBalancerDescription.targetGroups.protocol.missing");
}
if (targetGroup.getPort() == null) {
errors.rejectValue(
"targetGroups", "createAmazonLoadBalancerDescription.targetGroups.port.missing");
}
}
}
Set<String> unusedTargetGroupNames = new HashSet<>();
Expand Down Expand Up @@ -173,4 +179,17 @@ public void validate(
break;
}
}

private void validateLambdaTargetGroup(
UpsertAmazonLoadBalancerV2Description albDescription,
UpsertAmazonLoadBalancerV2Description.TargetGroup targetGroup,
Errors errors) {
// Add lambda specific validation, if required.
if (!AmazonLoadBalancerType.APPLICATION
.toString()
.equalsIgnoreCase(albDescription.getLoadBalancerType().toString())) {
errors.rejectValue(
"loadBalancerType", "createAmazonLoadBalancerDescription.loadBalancerType.invalid");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ enum OnDemandType {
Job,
TargetGroup,
CloudFormation,
Manifest;
Manifest,
Function;

static OnDemandType fromString(String s) {
return Arrays.stream(values())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,26 @@

package com.netflix.spinnaker.clouddriver.model;

import com.netflix.spinnaker.clouddriver.documentation.Empty;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;

public interface FunctionProvider {
Collection<? extends Function> getAllFunctions();

Function getFunction(String account, String region, String functionName);

/**
* Returns all functions related to an application based on one of the following criteria: - the
* load balancer name follows the Frigga naming conventions for load balancers (i.e., the load
* balancer name starts with the application name, followed by a hyphen)
*
* @param applicationName the name of the application
* @return a collection of functions.
*/
@Empty
default Set<? extends Function> getApplicationFunctions(String applicationName) {
return Collections.emptySet();
}
}
2 changes: 2 additions & 0 deletions clouddriver-lambda/clouddriver-lambda.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,6 @@ dependencies {
implementation "com.squareup.okhttp:okhttp-urlconnection"
implementation "com.squareup.retrofit:converter-jackson"
implementation "com.squareup.retrofit:retrofit"
implementation "com.netflix.spectator:spectator-api"
implementation "com.netflix.frigga:frigga"
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import com.amazonaws.services.lambda.model.AliasConfiguration;
import com.amazonaws.services.lambda.model.EventSourceMappingConfiguration;
import com.amazonaws.services.lambda.model.FunctionCodeLocation;
import com.amazonaws.services.lambda.model.FunctionConfiguration;
import com.netflix.spinnaker.clouddriver.model.Function;
import java.util.List;
Expand All @@ -33,4 +34,7 @@ public class LambdaFunction extends FunctionConfiguration implements Function {
private Map<String, String> revisions;
private List<AliasConfiguration> aliasConfigurations;
private List<EventSourceMappingConfiguration> eventSourceMappings;
private FunctionCodeLocation code;
private Map<String, String> tags;
private List<String> targetGroups;
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@

package com.netflix.spinnaker.clouddriver.lambda.deploy.description;

import com.amazonaws.services.lambda.model.DeadLetterConfig;
import com.amazonaws.services.lambda.model.TracingConfig;
import java.util.List;
import java.util.Map;
import lombok.Data;
import lombok.EqualsAndHashCode;

Expand All @@ -30,4 +34,13 @@ public class CreateLambdaFunctionConfigurationDescription
String role;
String runtime;
Integer timeout;
List<String> subnetIds;
List<String> securityGroupIds;
Map<String, String> envVariables;
Map<String, String> tags;
DeadLetterConfig deadLetterConfig;
String encryptionKMSKeyArn;
TracingConfig tracingConfig;
String targetGroups;
String runTime;
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package com.netflix.spinnaker.clouddriver.lambda.deploy.description;

import com.amazonaws.services.lambda.model.DeadLetterConfig;
import com.amazonaws.services.lambda.model.TracingConfig;
import java.util.List;
import java.util.Map;
import lombok.Data;
Expand All @@ -31,11 +33,22 @@ public class CreateLambdaFunctionDescription extends AbstractLambdaFunctionDescr
String handler;
String role;
String runtime;
String appName;

Integer memory;
Integer timeout;

List<Map<String, String>> tags;
Map<String, String> tags;

Boolean publish;

Map<String, String> envVariables;
List<String> subnetIds;
List<String> securityGroupIds;

String targetGroups;

DeadLetterConfig deadLetterConfig;
TracingConfig tracingConfig;
String encryptionKMSKeyArn;
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
public class UpdateLambdaFunctionCodeDescription extends AbstractLambdaFunctionDescription {
String functionName;

String s3Bucket;
String s3Key;
String s3bucket;
String s3key;
Boolean publish;
}
Loading

0 comments on commit 81d60a7

Please sign in to comment.