github.com/webonyx/up@v0.7.4-0.20180808230834-91b94e551323/platform/lambda/stack/resources/warming.go (about) 1 package resources 2 3 import ( 4 "fmt" 5 6 "github.com/apex/up/config" 7 "github.com/apex/up/internal/util" 8 ) 9 10 // warmingFunctionSource is the source code. 11 var warmingFunctionSource = ` 12 const http = require('https') 13 14 exports.handle = function(e, ctx, fn) { 15 const start = Date.now() 16 let pending = e.count 17 console.log('requesting %d', e.count) 18 19 for (let i = 0; i < e.count; i++) { 20 console.log('GET %s', e.url) 21 http.get(e.url, function (res) { 22 const d = Date.now() - start 23 console.log('GET %s -> %s (%dms)', e.url, res.statusCode, d) 24 --pending || fn() 25 }) 26 } 27 }` 28 29 // warming resources. 30 func warming(c *Config, m Map) { 31 for _, s := range c.Stages.List() { 32 if s.IsRemote() { 33 warmingStage(c, s, m) 34 } 35 } 36 } 37 38 // warmingStage sets up the warming resources for a given stage. 39 func warmingStage(c *Config, s *config.Stage, m Map) { 40 // TODO: refactor overrides so this is not necessary 41 g := c.Lambda 42 l := s.Lambda 43 44 if l.Warm == nil { 45 l.Warm = g.Warm 46 } 47 48 if l.WarmCount == 0 { 49 l.WarmCount = g.WarmCount 50 } 51 52 if l.WarmRate == 0 { 53 l.WarmRate = g.WarmRate 54 } 55 56 if l.Warm == nil || !*l.Warm { 57 return 58 } 59 60 warmingFunctionRole(c, m) 61 warmingFunction(c, m) 62 eventID := warmingStageEvent(c, s, &l, m) 63 warmingStageFunctionPermission(c, s, m, eventID) 64 } 65 66 // warmingStageFunctionPermission sets up function permissions. 67 func warmingStageFunctionPermission(c *Config, s *config.Stage, m Map, eventID string) { 68 id := util.Camelcase("warming_function_permission_%s", s.Name) 69 m[id] = Map{ 70 "Type": "AWS::Lambda::Permission", 71 "Properties": Map{ 72 "FunctionName": ref("WarmingFunction"), 73 "Action": "lambda:InvokeFunction", 74 "Principal": "events.amazonaws.com", 75 "SourceArn": get(eventID, "Arn"), 76 }, 77 } 78 } 79 80 // warmingStageEvent sets up a warming scheduled event. 81 func warmingStageEvent(c *Config, s *config.Stage, l *config.Lambda, m Map) string { 82 url := endpoint(s.Name) 83 input := join("", `{ "url": "`, url, fmt.Sprintf(`", "count": %d }`, l.WarmCount)) 84 id := util.Camelcase("warming_event_%s", s.Name) 85 86 m[id] = Map{ 87 "Type": "AWS::Events::Rule", 88 "Properties": Map{ 89 "State": "ENABLED", 90 "Description": util.ManagedByUp("Warming function scheduled event"), 91 "ScheduleExpression": rate(l.WarmRate), 92 "Targets": []Map{ 93 { 94 "Arn": get("WarmingFunction", "Arn"), 95 "Id": "WarmingFunction", 96 "Input": input, 97 }, 98 }, 99 }, 100 } 101 102 return id 103 } 104 105 // warmingFunction sets up a scheduled function for warming. 106 func warmingFunction(c *Config, m Map) { 107 m["WarmingFunction"] = Map{ 108 "Type": "AWS::Lambda::Function", 109 "Properties": Map{ 110 "FunctionName": fmt.Sprintf("%s-warming", c.Name), 111 "Description": util.ManagedByUp("Warming function"), 112 "Runtime": "nodejs6.10", 113 "Handler": "index.handle", 114 "Role": get("WarmingFunctionRole", "Arn"), 115 "MemorySize": 512, 116 "Timeout": 300, 117 "Code": Map{ 118 "ZipFile": warmingFunctionSource, 119 }, 120 }, 121 } 122 } 123 124 // warmingFunctionRole sets up the warming function role. 125 func warmingFunctionRole(c *Config, m Map) { 126 m["WarmingFunctionRole"] = Map{ 127 "Type": "AWS::IAM::Role", 128 "Properties": Map{ 129 "RoleName": fmt.Sprintf("%s-warming-function", c.Name), 130 "AssumeRolePolicyDocument": Map{ 131 "Version": "2012-10-17", 132 "Statement": []Map{ 133 { 134 "Effect": "Allow", 135 "Principal": Map{ 136 "Service": []string{"lambda.amazonaws.com"}, 137 }, 138 "Action": []string{"sts:AssumeRole"}, 139 }, 140 }, 141 }, 142 "Path": "/", 143 "Policies": []Map{ 144 { 145 "PolicyName": "root", 146 "PolicyDocument": Map{ 147 "Version": "2012-10-17", 148 "Statement": []Map{ 149 { 150 "Effect": "Allow", 151 "Action": []string{"logs:*"}, 152 "Resource": "arn:aws:logs:*:*:*", 153 }, 154 }, 155 }, 156 }, 157 }, 158 }, 159 } 160 } 161 162 // rate returns a rate string. 163 func rate(d config.Duration) string { 164 switch m := d.Seconds() / 60; { 165 case m == 1: 166 return "rate(1 minute)" 167 default: 168 return fmt.Sprintf("rate(%0.0f minutes)", m) 169 } 170 } 171 172 // endpoint returns the api endpoint for stage. 173 func endpoint(stage string) Map { 174 path := fmt.Sprintf("/%s/_ping", stage) 175 return join("", "https://", ref("Api"), ".execute-api.", ref("AWS::Region"), ".amazonaws.com", path) 176 }