Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

resource/aws_lambda_alias: Support Traffic Shifting #3316

Merged
merged 5 commits into from
Jul 2, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 67 additions & 5 deletions aws/resource_aws_lambda_alias.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package aws

import (
"errors"
"fmt"
"log"
"strings"
Expand All @@ -19,27 +20,41 @@ func resourceAwsLambdaAlias() *schema.Resource {
Delete: resourceAwsLambdaAliasDelete,

Schema: map[string]*schema.Schema{
"description": &schema.Schema{
"description": {
Type: schema.TypeString,
Optional: true,
},
"function_name": &schema.Schema{
"function_name": {
Type: schema.TypeString,
Required: true,
},
"function_version": &schema.Schema{
"function_version": {
Type: schema.TypeString,
Required: true,
},
"name": &schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"arn": &schema.Schema{
"arn": {
Type: schema.TypeString,
Computed: true,
},
"routing_config": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"additional_version_weights": {
Type: schema.TypeMap,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeFloat},
},
},
},
},
},
}
}
Expand All @@ -61,6 +76,10 @@ func resourceAwsLambdaAliasCreate(d *schema.ResourceData, meta interface{}) erro
Name: aws.String(aliasName),
}

if v, ok := d.GetOk("routing_config"); ok {
params.RoutingConfig = expandLambdaAliasRoutingConfiguration(v.([]interface{}))
}

aliasConfiguration, err := conn.CreateAlias(params)
if err != nil {
return fmt.Errorf("Error creating Lambda alias: %s", err)
Expand Down Expand Up @@ -99,6 +118,10 @@ func resourceAwsLambdaAliasRead(d *schema.ResourceData, meta interface{}) error
d.Set("name", aliasConfiguration.Name)
d.Set("arn", aliasConfiguration.AliasArn)

if err := d.Set("routing_config", flattenLambdaAliasRoutingConfiguration(aliasConfiguration.RoutingConfig)); err != nil {
return fmt.Errorf("error setting routing_config: %s", err)
}

return nil
}

Expand Down Expand Up @@ -134,6 +157,20 @@ func resourceAwsLambdaAliasUpdate(d *schema.ResourceData, meta interface{}) erro
FunctionName: aws.String(d.Get("function_name").(string)),
FunctionVersion: aws.String(d.Get("function_version").(string)),
Name: aws.String(d.Get("name").(string)),
RoutingConfig: &lambda.AliasRoutingConfiguration{},
}

if v, ok := d.GetOk("routing_config"); ok {
routingConfigs := v.([]interface{})
routingConfig, ok := routingConfigs[0].(map[string]interface{})
if !ok {
return errors.New("At least one field is expected inside routing_config")
}

if additionalVersionWeights, ok := routingConfig["additional_version_weights"]; ok {
weights := readAdditionalVersionWeights(additionalVersionWeights.(map[string]interface{}))
params.RoutingConfig.AdditionalVersionWeights = aws.Float64Map(weights)
}
}

_, err := conn.UpdateAlias(params)
Expand All @@ -143,3 +180,28 @@ func resourceAwsLambdaAliasUpdate(d *schema.ResourceData, meta interface{}) erro

return nil
}

func expandLambdaAliasRoutingConfiguration(l []interface{}) *lambda.AliasRoutingConfiguration {
aliasRoutingConfiguration := &lambda.AliasRoutingConfiguration{}

if len(l) == 0 || l[0] == nil {
return aliasRoutingConfiguration
}

m := l[0].(map[string]interface{})

if _, ok := m["additional_version_weights"]; ok {
aliasRoutingConfiguration.AdditionalVersionWeights = expandFloat64Map(m)
}

return aliasRoutingConfiguration
}

func readAdditionalVersionWeights(avw map[string]interface{}) map[string]float64 {
weights := make(map[string]float64)
for k, v := range avw {
weights[k] = v.(float64)
}

return weights
}
117 changes: 108 additions & 9 deletions aws/resource_aws_lambda_alias_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func TestAccAWSLambdaAlias_basic(t *testing.T) {
Providers: testAccProviders,
CheckDestroy: testAccCheckAwsLambdaAliasDestroy,
Steps: []resource.TestStep{
resource.TestStep{
{
Config: testAccAwsLambdaAliasConfig(roleName, policyName, attachmentName, funcName, aliasName),
Check: resource.ComposeTestCheckFunc(
testAccCheckAwsLambdaAliasExists("aws_lambda_alias.lambda_alias_test", &conf),
Expand Down Expand Up @@ -56,7 +56,7 @@ func TestAccAWSLambdaAlias_nameupdate(t *testing.T) {
Providers: testAccProviders,
CheckDestroy: testAccCheckAwsLambdaAliasDestroy,
Steps: []resource.TestStep{
resource.TestStep{
{
Config: testAccAwsLambdaAliasConfig(roleName, policyName, attachmentName, funcName, aliasName),
Check: resource.ComposeTestCheckFunc(
testAccCheckAwsLambdaAliasExists("aws_lambda_alias.lambda_alias_test", &conf),
Expand All @@ -65,7 +65,7 @@ func TestAccAWSLambdaAlias_nameupdate(t *testing.T) {
regexp.MustCompile(`^arn:aws:lambda:[a-z]+-[a-z]+-[0-9]+:\d{12}:function:`+funcName+`:`+aliasName+`$`)),
),
},
resource.TestStep{
{
Config: testAccAwsLambdaAliasConfig(roleName, policyName, attachmentName, funcName, aliasNameUpdate),
Check: resource.ComposeTestCheckFunc(
testAccCheckAwsLambdaAliasExists("aws_lambda_alias.lambda_alias_test", &conf),
Expand Down Expand Up @@ -142,6 +142,31 @@ func testAccCheckAwsLambdaAttributes(mapping *lambda.AliasConfiguration) resourc
}
}

func testAccCheckAwsLambdaAliasRoutingConfigExists(mapping *lambda.AliasConfiguration) resource.TestCheckFunc {
return func(s *terraform.State) error {
routingConfig := mapping.RoutingConfig

if routingConfig == nil {
return fmt.Errorf("Could not read Lambda alias routing config")
}
if len(routingConfig.AdditionalVersionWeights) != 1 {
return fmt.Errorf("Could not read Lambda alias additional version weights")
}
return nil
}
}

func testAccCheckAwsLambdaAliasRoutingConfigDoesNotExist(mapping *lambda.AliasConfiguration) resource.TestCheckFunc {
return func(s *terraform.State) error {
routingConfig := mapping.RoutingConfig

if routingConfig != nil {
return fmt.Errorf("Lambda alias routing config still exists after removal")
}
return nil
}
}

func testAccAwsLambdaAliasConfig(roleName, policyName, attachmentName, funcName, aliasName string) string {
return fmt.Sprintf(`
resource "aws_iam_role" "iam_for_lambda" {
Expand Down Expand Up @@ -192,17 +217,91 @@ resource "aws_iam_policy_attachment" "policy_attachment_for_role" {
}

resource "aws_lambda_function" "lambda_function_test_create" {
filename = "test-fixtures/lambdatest.zip"
function_name = "%s"
role = "${aws_iam_role.iam_for_lambda.arn}"
handler = "exports.example"
runtime = "nodejs4.3"
filename = "test-fixtures/lambdatest.zip"
function_name = "%s"
role = "${aws_iam_role.iam_for_lambda.arn}"
handler = "exports.example"
runtime = "nodejs4.3"
source_code_hash = "${base64sha256(file("test-fixtures/lambdatest.zip"))}"
publish = "true"
}

resource "aws_lambda_alias" "lambda_alias_test" {
name = "%s"
description = "a sample description"
function_name = "${aws_lambda_function.lambda_function_test_create.arn}"
function_version = "1"
}`, roleName, policyName, attachmentName, funcName, aliasName)
}

func testAccAwsLambdaAliasConfigWithRoutingConfig(roleName, policyName, attachmentName, funcName, aliasName string) string {
return fmt.Sprintf(`
resource "aws_iam_role" "iam_for_lambda" {
name = "%s"

assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}

resource "aws_iam_policy" "policy_for_role" {
name = "%s"
path = "/"
description = "IAM policy for for Lamda alias testing"

policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"lambda:*"
],
"Resource": "*"
}
]
}
EOF
}

resource "aws_iam_policy_attachment" "policy_attachment_for_role" {
name = "%s"
roles = ["${aws_iam_role.iam_for_lambda.name}"]
policy_arn = "${aws_iam_policy.policy_for_role.arn}"
}

resource "aws_lambda_function" "lambda_function_test_create" {
filename = "test-fixtures/lambdatest_modified.zip"
function_name = "%s"
role = "${aws_iam_role.iam_for_lambda.arn}"
handler = "exports.example"
runtime = "nodejs4.3"
source_code_hash = "${base64sha256(file("test-fixtures/lambdatest_modified.zip"))}"
publish = "true"
}

resource "aws_lambda_alias" "lambda_alias_test" {
name = "%s"
description = "a sample description"
function_name = "${aws_lambda_function.lambda_function_test_create.arn}"
function_version = "$LATEST"
function_version = "1"
routing_config = {
additional_version_weights = {
"2" = 0.5
}
}
}`, roleName, policyName, attachmentName, funcName, aliasName)
}
33 changes: 27 additions & 6 deletions aws/structure.go
Original file line number Diff line number Diff line change
Expand Up @@ -792,6 +792,15 @@ func expandStringList(configured []interface{}) []*string {
return vs
}

// Expands a map of string to interface to a map of string to *float
func expandFloat64Map(m map[string]interface{}) map[string]*float64 {
float64Map := make(map[string]*float64, len(m))
for k, v := range m {
float64Map[k] = aws.Float64(v.(float64))
}
return float64Map
}

// Takes the result of schema.Set of strings and returns a []*string
func expandStringSet(configured *schema.Set) []*string {
return expandStringList(configured.List())
Expand Down Expand Up @@ -1302,6 +1311,18 @@ func flattenLambdaVpcConfigResponse(s *lambda.VpcConfigResponse) []map[string]in
return []map[string]interface{}{settings}
}

func flattenLambdaAliasRoutingConfiguration(arc *lambda.AliasRoutingConfiguration) []interface{} {
if arc == nil {
return []interface{}{}
}

m := map[string]interface{}{
"additional_version_weights": aws.Float64ValueMap(arc.AdditionalVersionWeights),
}

return []interface{}{m}
}

func flattenDSConnectSettings(
customerDnsIps []*string,
s *directoryservice.DirectoryConnectSettingsDescription) []map[string]interface{} {
Expand Down Expand Up @@ -1450,7 +1471,7 @@ func expandApiGatewayRequestResponseModelOperations(d *schema.ResourceData, key
oldModelMap := oldModels.(map[string]interface{})
newModelMap := newModels.(map[string]interface{})

for k, _ := range oldModelMap {
for k := range oldModelMap {
operation := apigateway.PatchOperation{
Op: aws.String("remove"),
Path: aws.String(fmt.Sprintf("/%s/%s", prefix, strings.Replace(k, "/", "~1", -1))),
Expand All @@ -1468,7 +1489,7 @@ func expandApiGatewayRequestResponseModelOperations(d *schema.ResourceData, key

for nK, nV := range newModelMap {
exists := false
for k, _ := range oldModelMap {
for k := range oldModelMap {
if k == nK {
exists = true
}
Expand Down Expand Up @@ -1502,7 +1523,7 @@ func deprecatedExpandApiGatewayMethodParametersJSONOperations(d *schema.Resource
return operations, err
}

for k, _ := range oldParametersMap {
for k := range oldParametersMap {
operation := apigateway.PatchOperation{
Op: aws.String("remove"),
Path: aws.String(fmt.Sprintf("/%s/%s", prefix, k)),
Expand All @@ -1520,7 +1541,7 @@ func deprecatedExpandApiGatewayMethodParametersJSONOperations(d *schema.Resource

for nK, nV := range newParametersMap {
exists := false
for k, _ := range oldParametersMap {
for k := range oldParametersMap {
if k == nK {
exists = true
}
Expand All @@ -1545,7 +1566,7 @@ func expandApiGatewayMethodParametersOperations(d *schema.ResourceData, key stri
oldParametersMap := oldParameters.(map[string]interface{})
newParametersMap := newParameters.(map[string]interface{})

for k, _ := range oldParametersMap {
for k := range oldParametersMap {
operation := apigateway.PatchOperation{
Op: aws.String("remove"),
Path: aws.String(fmt.Sprintf("/%s/%s", prefix, k)),
Expand All @@ -1568,7 +1589,7 @@ func expandApiGatewayMethodParametersOperations(d *schema.ResourceData, key stri

for nK, nV := range newParametersMap {
exists := false
for k, _ := range oldParametersMap {
for k := range oldParametersMap {
if k == nK {
exists = true
}
Expand Down
Binary file added aws/test-fixtures/lambdatest_modified.zip
Binary file not shown.
Loading