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 }