github.com/franciscocpg/up@v0.1.10/platform/lambda/stack/resources.go (about)

     1  package stack
     2  
     3  import (
     4  	"fmt"
     5  	"strconv"
     6  
     7  	"github.com/apex/up"
     8  	"github.com/apex/up/internal/util"
     9  )
    10  
    11  // Map .
    12  type Map map[string]interface{}
    13  
    14  // ref of id.
    15  func ref(id string) Map {
    16  	return Map{
    17  		"Ref": id,
    18  	}
    19  }
    20  
    21  // get value from named ref.
    22  func get(name, value string) Map {
    23  	return Map{
    24  		"Fn::GetAtt": []string{
    25  			name,
    26  			value,
    27  		},
    28  	}
    29  }
    30  
    31  // join strings with delim.
    32  func join(delim string, s ...interface{}) Map {
    33  	return Map{
    34  		"Fn::Join": []interface{}{
    35  			delim,
    36  			s,
    37  		},
    38  	}
    39  }
    40  
    41  // stageVariable by name.
    42  func stageVariable(name string) string {
    43  	return fmt.Sprintf("${stageVariables.%s}", name)
    44  }
    45  
    46  // lambda ARN for function name.
    47  func lambdaArn(name string) Map {
    48  	return join(":", "arn", "aws", "lambda", ref("AWS::Region"), ref("AWS::AccountId"), "function", ref(name))
    49  }
    50  
    51  // lambda ARN for function name with qualifier.
    52  func lambdaArnQualifier(name, qualifier string) Map {
    53  	return join(":", "arn", "aws", "lambda", ref("AWS::Region"), ref("AWS::AccountId"), "function", join(":", ref(name), qualifier))
    54  }
    55  
    56  // DNS resources.
    57  func dns(c *up.Config, m Map) {
    58  	for _, z := range c.DNS.Zones {
    59  		zoneID := util.Camelcase("dns_zone_%s", z.Name)
    60  
    61  		m[zoneID] = Map{
    62  			"Type": "AWS::Route53::HostedZone",
    63  			"Properties": Map{
    64  				"Name": z.Name,
    65  			},
    66  		}
    67  
    68  		for _, r := range z.Records {
    69  			id := util.Camelcase("dns_zone_%s_record_%s", z.Name, r.Name)
    70  
    71  			m[id] = Map{
    72  				"Type": "AWS::Route53::RecordSet",
    73  				"Properties": Map{
    74  					"Name":            r.Name,
    75  					"Type":            r.Type,
    76  					"TTL":             strconv.Itoa(r.TTL),
    77  					"ResourceRecords": r.Value,
    78  					"HostedZoneId":    ref(zoneID),
    79  				},
    80  			}
    81  		}
    82  	}
    83  }
    84  
    85  // API resources.
    86  func api(c *up.Config, m Map) {
    87  	desc := util.ManagedByUp(c.Description)
    88  
    89  	m["Api"] = Map{
    90  		"Type": "AWS::ApiGateway::RestApi",
    91  		"Properties": Map{
    92  			"Name":        ref("Name"),
    93  			"Description": desc,
    94  			"BinaryMediaTypes": []string{
    95  				"*/*",
    96  			},
    97  		},
    98  	}
    99  
   100  	integration := Map{
   101  		"Type":                  "AWS_PROXY",
   102  		"IntegrationHttpMethod": "POST",
   103  		"Uri": join("",
   104  			"arn:aws:apigateway:",
   105  			ref("AWS::Region"),
   106  			":lambda:path/2015-03-31/functions/",
   107  			lambdaArnQualifier("FunctionName", stageVariable("qualifier")),
   108  			"/invocations"),
   109  	}
   110  
   111  	m["ApiRootMethod"] = Map{
   112  		"Type": "AWS::ApiGateway::Method",
   113  		"Properties": Map{
   114  			"RestApiId":         ref("Api"),
   115  			"ResourceId":        get("Api", "RootResourceId"),
   116  			"HttpMethod":        "ANY",
   117  			"AuthorizationType": "NONE",
   118  			"Integration":       integration,
   119  		},
   120  	}
   121  
   122  	m["ApiProxyResource"] = Map{
   123  		"Type": "AWS::ApiGateway::Resource",
   124  		"Properties": Map{
   125  			"RestApiId": ref("Api"),
   126  			"ParentId":  get("Api", "RootResourceId"),
   127  			"PathPart":  "{proxy+}",
   128  		},
   129  	}
   130  
   131  	m["ApiProxyMethod"] = Map{
   132  		"Type": "AWS::ApiGateway::Method",
   133  		"Properties": Map{
   134  			"RestApiId":         ref("Api"),
   135  			"ResourceId":        ref("ApiProxyResource"),
   136  			"HttpMethod":        "ANY",
   137  			"AuthorizationType": "NONE",
   138  			"Integration":       integration,
   139  		},
   140  	}
   141  
   142  	// TODO: allow mapping in config
   143  	m["ApiDeploymentDevelopment"] = Map{
   144  		"Type":      "AWS::ApiGateway::Deployment",
   145  		"DependsOn": []string{"ApiRootMethod", "ApiProxyMethod", "ApiFunctionAliasDevelopment"},
   146  		"Properties": Map{
   147  			"RestApiId": ref("Api"),
   148  			"StageName": "development",
   149  			"StageDescription": Map{
   150  				"Variables": Map{
   151  					"qualifier": "development",
   152  				},
   153  			},
   154  		},
   155  	}
   156  
   157  	m["ApiDeploymentStaging"] = Map{
   158  		"Type":      "AWS::ApiGateway::Deployment",
   159  		"DependsOn": []string{"ApiRootMethod", "ApiProxyMethod", "ApiFunctionAliasStaging"},
   160  		"Properties": Map{
   161  			"RestApiId": ref("Api"),
   162  			"StageName": "staging",
   163  			"StageDescription": Map{
   164  				"Variables": Map{
   165  					"qualifier": "staging",
   166  				},
   167  			},
   168  		},
   169  	}
   170  
   171  	m["ApiDeploymentProduction"] = Map{
   172  		"Type":      "AWS::ApiGateway::Deployment",
   173  		"DependsOn": []string{"ApiRootMethod", "ApiProxyMethod", "ApiFunctionAliasProduction"},
   174  		"Properties": Map{
   175  			"RestApiId": ref("Api"),
   176  			"StageName": "production",
   177  			"StageDescription": Map{
   178  				"Variables": Map{
   179  					"qualifier": "production",
   180  				},
   181  			},
   182  		},
   183  	}
   184  
   185  	m["ApiFunctionAliasDevelopment"] = Map{
   186  		"Type": "AWS::Lambda::Alias",
   187  		"Properties": Map{
   188  			"Name":            "development",
   189  			"Description":     util.ManagedByUp("Development environment"),
   190  			"FunctionName":    ref("FunctionName"),
   191  			"FunctionVersion": "$LATEST",
   192  		},
   193  	}
   194  
   195  	m["ApiFunctionAliasStaging"] = Map{
   196  		"Type": "AWS::Lambda::Alias",
   197  		"Properties": Map{
   198  			"Name":            "staging",
   199  			"Description":     util.ManagedByUp("Staging environment"),
   200  			"FunctionName":    ref("FunctionName"),
   201  			"FunctionVersion": ref("FunctionVersion"),
   202  		},
   203  	}
   204  
   205  	m["ApiFunctionAliasProduction"] = Map{
   206  		"Type": "AWS::Lambda::Alias",
   207  		"Properties": Map{
   208  			"Name":            "production",
   209  			"Description":     util.ManagedByUp("Production environment"),
   210  			"FunctionName":    ref("FunctionName"),
   211  			"FunctionVersion": ref("FunctionVersion"),
   212  		},
   213  	}
   214  }
   215  
   216  // IAM resources.
   217  func iam(c *up.Config, m Map) {
   218  	m["ApiLambdaPermissionDevelopment"] = Map{
   219  		"Type":      "AWS::Lambda::Permission",
   220  		"DependsOn": "ApiFunctionAliasDevelopment",
   221  		"Properties": Map{
   222  			"Action":       "lambda:invokeFunction",
   223  			"FunctionName": lambdaArnQualifier("FunctionName", "development"),
   224  			"Principal":    "apigateway.amazonaws.com",
   225  			"SourceArn": join("",
   226  				"arn:aws:execute-api",
   227  				":",
   228  				ref("AWS::Region"),
   229  				":",
   230  				ref("AWS::AccountId"),
   231  				":",
   232  				ref("Api"),
   233  				"/*"),
   234  		},
   235  	}
   236  
   237  	m["ApiLambdaPermissionStaging"] = Map{
   238  		"Type":      "AWS::Lambda::Permission",
   239  		"DependsOn": "ApiFunctionAliasStaging",
   240  		"Properties": Map{
   241  			"Action":       "lambda:invokeFunction",
   242  			"FunctionName": lambdaArnQualifier("FunctionName", "staging"),
   243  			"Principal":    "apigateway.amazonaws.com",
   244  			"SourceArn": join("",
   245  				"arn:aws:execute-api",
   246  				":",
   247  				ref("AWS::Region"),
   248  				":",
   249  				ref("AWS::AccountId"),
   250  				":",
   251  				ref("Api"),
   252  				"/*"),
   253  		},
   254  	}
   255  
   256  	m["ApiLambdaPermissionProduction"] = Map{
   257  		"Type":      "AWS::Lambda::Permission",
   258  		"DependsOn": "ApiFunctionAliasProduction",
   259  		"Properties": Map{
   260  			"Action":       "lambda:invokeFunction",
   261  			"FunctionName": lambdaArnQualifier("FunctionName", "production"),
   262  			"Principal":    "apigateway.amazonaws.com",
   263  			"SourceArn": join("",
   264  				"arn:aws:execute-api",
   265  				":",
   266  				ref("AWS::Region"),
   267  				":",
   268  				ref("AWS::AccountId"),
   269  				":",
   270  				ref("Api"),
   271  				"/*"),
   272  		},
   273  	}
   274  }
   275  
   276  // ACM resources.
   277  func acm(c *up.Config, m Map) {
   278  	for _, c := range c.Certs {
   279  		domain := c.Domains[0]
   280  		alts := c.Domains[1:]
   281  		name := util.Camelcase("cert_%s", domain)
   282  
   283  		props := Map{
   284  			"DomainName": domain,
   285  		}
   286  
   287  		if len(alts) > 0 {
   288  			props["SubjectAlternativeNames"] = alts
   289  		}
   290  
   291  		m[name] = Map{
   292  			"Type":       "AWS::CertificateManager::Certificate",
   293  			"Properties": props,
   294  		}
   295  	}
   296  }
   297  
   298  // resources of the stack.
   299  func resources(c *up.Config) Map {
   300  	m := Map{}
   301  
   302  	api(c, m)
   303  	iam(c, m)
   304  	acm(c, m)
   305  	dns(c, m)
   306  
   307  	return m
   308  }
   309  
   310  // parameters of the stack.
   311  func parameters(c *up.Config) Map {
   312  	return Map{
   313  		"Name": Map{
   314  			"Description": "Name of application",
   315  			"Type":        "String",
   316  		},
   317  		"FunctionName": Map{
   318  			"Description": "Name of application function",
   319  			"Type":        "String",
   320  		},
   321  		"FunctionVersion": Map{
   322  			"Description": "Version of application function",
   323  			"Type":        "String",
   324  		},
   325  	}
   326  }
   327  
   328  // outputs of the stack.
   329  func outputs(c *up.Config) Map {
   330  	return Map{
   331  		"ApiName": Map{
   332  			"Description": "API name",
   333  			"Value":       ref("Name"),
   334  		},
   335  		"ApiFunctionName": Map{
   336  			"Description": "API Lambda function name",
   337  			"Value":       ref("FunctionName"),
   338  		},
   339  		"ApiFunctionArn": Map{
   340  			"Description": "API Lambda function ARN",
   341  			"Value":       lambdaArn("FunctionName"),
   342  		},
   343  	}
   344  }
   345  
   346  // template for the given config.
   347  func template(c *up.Config) Map {
   348  	return Map{
   349  		"AWSTemplateFormatVersion": "2010-09-09",
   350  		"Parameters":               parameters(c),
   351  		"Outputs":                  outputs(c),
   352  		"Resources":                resources(c),
   353  	}
   354  }