modulator 0.2.2 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +6 -0
- data/CHANGELOG.md +19 -0
- data/Gemfile +0 -4
- data/Gemfile.lock +26 -22
- data/README.md +243 -7
- data/lib/modulator.rb +82 -46
- data/lib/modulator/gateway/gateway.rb +4 -6
- data/lib/modulator/gateway/routes/console.rb +32 -34
- data/lib/modulator/gateway_event.json +60 -0
- data/lib/modulator/{lambda/aws_lambda_handler.rb → lambda_handler.rb} +2 -15
- data/lib/modulator/stack/builder.rb +256 -0
- data/lib/modulator/stack/policies.rb +99 -0
- data/lib/modulator/{lambda/aws_stack_uploader.rb → stack/uploader.rb} +59 -38
- data/lib/modulator/version.rb +1 -1
- data/lib/utils.rb +7 -1
- data/modulator.gemspec +1 -4
- metadata +9 -20
- data/lib/modulator/lambda/aws_stack_builder.rb +0 -225
@@ -12,7 +12,7 @@ class Gateway < Roda
|
|
12
12
|
|
13
13
|
my_dir = Pathname.new(__FILE__).dirname
|
14
14
|
my_dir.glob('routes/*.rb').each{|file| require_relative file}
|
15
|
-
DUMMY_AWS_EVENT = Utils.load_json my_dir.parent.join('
|
15
|
+
DUMMY_AWS_EVENT = Utils.load_json my_dir.parent.join('gateway_event.json')
|
16
16
|
|
17
17
|
before do
|
18
18
|
@time = Time.now
|
@@ -32,7 +32,7 @@ class Gateway < Roda
|
|
32
32
|
after do |res|
|
33
33
|
puts "Matched path: #{request.matched_path}"
|
34
34
|
puts "Status: #{response.status}"
|
35
|
-
puts
|
35
|
+
puts "Took: #{Time.now - @time} seconds"
|
36
36
|
end
|
37
37
|
|
38
38
|
plugin :error_handler do |e|
|
@@ -51,11 +51,9 @@ class Gateway < Roda
|
|
51
51
|
|
52
52
|
# process lambda configs
|
53
53
|
Modulator::LAMBDAS.each do |lambda_name, lambda_config|
|
54
|
-
# puts "* Registering #{lambda_name}"
|
55
|
-
# pp lambda_config
|
56
54
|
|
57
|
-
#
|
58
|
-
Modulator.
|
55
|
+
# copy config to env
|
56
|
+
Modulator.set_env_values lambda_config
|
59
57
|
|
60
58
|
# build route
|
61
59
|
@path_params = {}
|
@@ -1,11 +1,5 @@
|
|
1
|
-
require 'aws-sdk-cloudformation'
|
2
|
-
|
3
|
-
# console API
|
4
1
|
Gateway.route('console') do |r|
|
5
2
|
|
6
|
-
client = Aws::CloudFormation::Client.new
|
7
|
-
app_name = (opts[:app_dir] || Pathname.getwd.basename.to_s).camelize
|
8
|
-
|
9
3
|
# helpers
|
10
4
|
def capture_output
|
11
5
|
previous_stdout, $stdout = $stdout, StringIO.new
|
@@ -18,18 +12,29 @@ Gateway.route('console') do |r|
|
|
18
12
|
$stderr = previous_stderr
|
19
13
|
end
|
20
14
|
|
21
|
-
|
22
|
-
|
23
|
-
|
15
|
+
# cf call return value or its capture from stdout
|
16
|
+
def render_cf_call_output(cf_call_result, cf_call_output, cf_call_name = 'aws cf sdk call')
|
17
|
+
if cf_call_result
|
18
|
+
cf_call_result.to_hash
|
24
19
|
else
|
25
|
-
{
|
20
|
+
{cf_call_name => cf_call_output.split("\n").first}
|
26
21
|
end
|
27
22
|
end
|
28
23
|
|
24
|
+
# list registered lambdas
|
25
|
+
r.on 'lambdas' do
|
26
|
+
r.get 'list' do
|
27
|
+
Modulator::LAMBDAS
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# stack operations
|
29
32
|
r.on 'stack' do
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
+
client = Aws::CloudFormation::Client.new
|
34
|
+
app_name = (opts[:app_dir] || Pathname.getwd.basename.to_s).camelize
|
35
|
+
s3_bucket = opts[:s3_bucket] || ENV['MODULATOR_S3_BUCKET'] || 'modulator-apps'
|
36
|
+
payload = request.params.symbolize_keys
|
37
|
+
cf_call_result = nil
|
33
38
|
|
34
39
|
r.get 'events' do
|
35
40
|
resp = client.describe_stack_events(stack_name: app_name, next_token: @headers['X-Next-Token'])
|
@@ -37,6 +42,7 @@ Gateway.route('console') do |r|
|
|
37
42
|
resp.stack_events.map(&:to_hash)
|
38
43
|
end
|
39
44
|
|
45
|
+
# initialize stack
|
40
46
|
r.on 'init' do
|
41
47
|
serializer = :yaml
|
42
48
|
content_type = 'text/html'
|
@@ -47,42 +53,34 @@ Gateway.route('console') do |r|
|
|
47
53
|
r.pass
|
48
54
|
end
|
49
55
|
|
50
|
-
# init stack
|
51
56
|
stack = Modulator.init_stack(
|
52
|
-
|
53
|
-
bucket: bucket_name,
|
57
|
+
s3_bucket: s3_bucket,
|
54
58
|
timeout: 15
|
55
59
|
)
|
56
60
|
|
57
|
-
#
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
type: param[:type],
|
62
|
-
value: param[:value]
|
63
|
-
)
|
64
|
-
end
|
65
|
-
|
66
|
-
r.post 'valid' do
|
67
|
-
command_output = capture_output do
|
68
|
-
command_result = stack.valid?
|
61
|
+
# validate stack
|
62
|
+
r.post 'validate' do
|
63
|
+
cf_call_output = capture_output do
|
64
|
+
cf_call_result = stack.valid?
|
69
65
|
end
|
70
|
-
|
66
|
+
render_cf_call_output(cf_call_result, cf_call_output, 'stack validation call')
|
71
67
|
end
|
72
68
|
|
69
|
+
# deploy stack
|
73
70
|
r.post 'deploy' do
|
74
|
-
|
75
|
-
|
76
|
-
parameters:
|
71
|
+
cf_call_output = capture_output do
|
72
|
+
cf_call_result = stack.deploy(
|
73
|
+
parameters: s3_bucket[:parameters]&.map{|param| {parameter_key: param[:key], parameter_value: param[:value]}},
|
77
74
|
capabilities: ['CAPABILITY_IAM']
|
78
75
|
)
|
79
76
|
end
|
80
|
-
|
77
|
+
render_cf_call_output(cf_call_result, cf_call_output)
|
81
78
|
end
|
82
79
|
|
80
|
+
# print template
|
83
81
|
r.post do
|
84
82
|
response['Content-Type'] = content_type
|
85
|
-
|
83
|
+
stack.to_cf(serializer)
|
86
84
|
end
|
87
85
|
end
|
88
86
|
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
{
|
2
|
+
"body": "{\"x\": 123}",
|
3
|
+
"isBase64Encoded": false,
|
4
|
+
|
5
|
+
"resource": "/calc/{proxy+}",
|
6
|
+
"path": "/calc/1/add/2",
|
7
|
+
"httpMethod": "POST",
|
8
|
+
|
9
|
+
"multiValueHeaders": {
|
10
|
+
"x-custom": [
|
11
|
+
"abc"
|
12
|
+
]
|
13
|
+
},
|
14
|
+
"queryStringParameters": {
|
15
|
+
"aa": "11",
|
16
|
+
"bb": "22"
|
17
|
+
},
|
18
|
+
"multiValueQueryStringParameters": {
|
19
|
+
"aa": [
|
20
|
+
"11"
|
21
|
+
],
|
22
|
+
"bb": [
|
23
|
+
"22"
|
24
|
+
]
|
25
|
+
},
|
26
|
+
"pathParameters": {
|
27
|
+
"id": "1",
|
28
|
+
"other_id": "2"
|
29
|
+
},
|
30
|
+
"stageVariables": null,
|
31
|
+
|
32
|
+
"headers": {
|
33
|
+
"Host": "1234567890.execute-api.us-east-1.amazonaws.com",
|
34
|
+
"User-Agent": "Custom User Agent String",
|
35
|
+
"X-Forwarded-For": "127.0.0.1, 127.0.0.2",
|
36
|
+
"X-Forwarded-Port": "443",
|
37
|
+
"X-Forwarded-Proto": "https"
|
38
|
+
},
|
39
|
+
"requestContext": {
|
40
|
+
"accountId": "123456789012",
|
41
|
+
"resourceId": "123456",
|
42
|
+
"stage": "prod",
|
43
|
+
"requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef",
|
44
|
+
"requestTime": "09/Apr/2015:12:34:56 +0000",
|
45
|
+
"requestTimeEpoch": 1428582896000,
|
46
|
+
"identity": {
|
47
|
+
"cognitoIdentityPoolId": null,
|
48
|
+
"accountId": null,
|
49
|
+
"cognitoIdentityId": null,
|
50
|
+
"caller": null,
|
51
|
+
"accessKey": null,
|
52
|
+
"sourceIp": "127.0.0.1",
|
53
|
+
"cognitoAuthenticationType": null,
|
54
|
+
"cognitoAuthenticationProvider": null,
|
55
|
+
"userArn": null,
|
56
|
+
"userAgent": "Custom User Agent String",
|
57
|
+
"user": null
|
58
|
+
}
|
59
|
+
}
|
60
|
+
}
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'pathname'
|
2
2
|
|
3
|
+
# NOTE: aws has LambdaHandler already defined as a class so we need an aws prefix here
|
3
4
|
module AwsLambdaHandler
|
4
5
|
module_function
|
5
6
|
|
@@ -8,20 +9,6 @@ module AwsLambdaHandler
|
|
8
9
|
# TODO: implement handlers for other event types based on some event key, like AwsS3EventHandler
|
9
10
|
AwsApiGatewayEventHandler.call(event: event, context: context)
|
10
11
|
end
|
11
|
-
|
12
|
-
# helpers
|
13
|
-
def symbolize_keys(obj)
|
14
|
-
case obj
|
15
|
-
when Hash
|
16
|
-
hash = {}
|
17
|
-
obj.each {|k, v| hash[k.to_sym] = symbolize_keys(v)}
|
18
|
-
hash
|
19
|
-
when Array
|
20
|
-
obj.map {|x| symbolize_keys(x)}
|
21
|
-
else
|
22
|
-
obj
|
23
|
-
end
|
24
|
-
end
|
25
12
|
end
|
26
13
|
|
27
14
|
module AwsApiGatewayEventHandler
|
@@ -96,7 +83,7 @@ module AwsApiGatewayEventHandler
|
|
96
83
|
mod.send(mod_method, *path_params.values)
|
97
84
|
|
98
85
|
elsif verb == 'POST'
|
99
|
-
payload =
|
86
|
+
payload = JSON.parse(event['body'], symbolize_names: true)
|
100
87
|
method_signature.each do |arg_type, arg_name| # [[:req, :id], [:key, :pet]]
|
101
88
|
payload = {arg_name => payload} if arg_type == :key # scope payload to first named argument
|
102
89
|
end
|
@@ -0,0 +1,256 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'humidifier'
|
3
|
+
require_relative 'uploader'
|
4
|
+
require_relative 'policies'
|
5
|
+
|
6
|
+
module StackBuilder
|
7
|
+
module_function
|
8
|
+
|
9
|
+
RUBY_VERSION = 'ruby2.5'
|
10
|
+
GEM_PATH_RUBY_VERSION = '2.5.0'
|
11
|
+
GEM_PATH = "/opt/ruby/#{GEM_PATH_RUBY_VERSION}"
|
12
|
+
LAMBDA_HANDLER_FILE_NAME = 'modulator-lambda-handler'
|
13
|
+
|
14
|
+
class << self
|
15
|
+
attr_accessor :stack, :stack_opts, :app_name, :app_path, :app_dir
|
16
|
+
attr_accessor :hidden_dir, :s3_bucket, :lambda_handler_s3_object_version
|
17
|
+
attr_accessor :api_gateway_deployment, :api_gateway_id, :lambda_policies
|
18
|
+
attr_accessor :lambda_handlers, :lambda_handler_s3_key
|
19
|
+
end
|
20
|
+
|
21
|
+
def init(app_name:, s3_bucket:, **stack_opts)
|
22
|
+
puts 'Initializing stack'
|
23
|
+
@app_name = app_name.camelize
|
24
|
+
@s3_bucket = s3_bucket
|
25
|
+
@app_path = Pathname.getwd
|
26
|
+
@app_dir = app_path.basename.to_s
|
27
|
+
@hidden_dir = '.modulator'
|
28
|
+
@stack_opts = stack_opts
|
29
|
+
@lambda_handlers = stack_opts[:lambda_handlers] || []
|
30
|
+
@lambda_policies = Array(stack_opts[:lambda_policies]) << :cloudwatch
|
31
|
+
|
32
|
+
# create hidden dir for build artifacts
|
33
|
+
app_path.join(hidden_dir).mkpath
|
34
|
+
|
35
|
+
# init stack instance
|
36
|
+
self.stack = Humidifier::Stack.new(name: app_name, aws_template_format_version: '2010-09-09')
|
37
|
+
|
38
|
+
# app environment - test, development, production ...
|
39
|
+
app_envs = stack_opts[:app_envs] || ['development']
|
40
|
+
stack.add_parameter('AppEnvironment', description: 'Application environment', type: 'String', allowed_values: app_envs, constraint_description: "Must be one of #{app_envs.join(', ')}")
|
41
|
+
|
42
|
+
if lambda_handlers.empty?
|
43
|
+
# api stage
|
44
|
+
stack.add_parameter('ApiGatewayStageName', description: 'Gateway deployment stage', type: 'String', default: 'v1')
|
45
|
+
|
46
|
+
# add gateway
|
47
|
+
stack.add_api_gateway
|
48
|
+
stack.add_api_gateway_deployment
|
49
|
+
end
|
50
|
+
|
51
|
+
# add role
|
52
|
+
stack.add_lambda_iam_role
|
53
|
+
|
54
|
+
# add policies to role
|
55
|
+
stack.lambda_policies.each do |policy|
|
56
|
+
stack.add_policy(policy) if policy.is_a?(Symbol)
|
57
|
+
stack.add_policy(policy[:name], **policy) if policy.is_a?(Hash)
|
58
|
+
end
|
59
|
+
|
60
|
+
# simple lambda app
|
61
|
+
if lambda_handlers.any?
|
62
|
+
stack.upload_lambda_files
|
63
|
+
lambda_handlers.each do |handler|
|
64
|
+
stack.add_lambda(handler: handler, env: stack_opts[:env] || {}, settings: stack_opts[:settings] || {})
|
65
|
+
end
|
66
|
+
else
|
67
|
+
# upload handlers and layers
|
68
|
+
stack.upload_files
|
69
|
+
end
|
70
|
+
|
71
|
+
# return humidifier instance
|
72
|
+
stack
|
73
|
+
end
|
74
|
+
|
75
|
+
def upload_files
|
76
|
+
if stack_opts[:skip_upload]
|
77
|
+
puts 'Skipping upload'
|
78
|
+
return
|
79
|
+
end
|
80
|
+
stack.upload_generic_lambda_handler
|
81
|
+
puts 'Generating layers'
|
82
|
+
stack.upload_gems_layer
|
83
|
+
stack.upload_app_layer
|
84
|
+
end
|
85
|
+
|
86
|
+
def add_lambda_endpoint(**opts) # gateway:, mod:, wrapper: {}, env: {}, settings: {}
|
87
|
+
# add api resources and its lambda
|
88
|
+
stack.add_api_gateway_resources(gateway: opts[:gateway], lambda: stack.add_generic_lambda(opts))
|
89
|
+
end
|
90
|
+
|
91
|
+
# gateway
|
92
|
+
def add_api_gateway
|
93
|
+
self.api_gateway_id = 'ApiGateway'
|
94
|
+
stack.add(api_gateway_id, Humidifier::ApiGateway::RestApi.new(name: app_name, description: app_name + ' API'))
|
95
|
+
end
|
96
|
+
|
97
|
+
# gateway deployment
|
98
|
+
def add_api_gateway_deployment
|
99
|
+
self.api_gateway_deployment = Humidifier::ApiGateway::Deployment.new(
|
100
|
+
rest_api_id: Humidifier.ref(api_gateway_id),
|
101
|
+
stage_name: Humidifier.ref("ApiGatewayStageName")
|
102
|
+
)
|
103
|
+
stack.add('ApiGatewayDeployment', api_gateway_deployment)
|
104
|
+
stack.add_output('ApiGatewayInvokeURL',
|
105
|
+
value: Humidifier.fn.sub("https://${#{api_gateway_id}}.execute-api.${AWS::Region}.amazonaws.com/${ApiGatewayStageName}"),
|
106
|
+
description: 'API root url',
|
107
|
+
export_name: app_name + 'RootUrl'
|
108
|
+
)
|
109
|
+
api_gateway_deployment.depends_on = []
|
110
|
+
end
|
111
|
+
|
112
|
+
# custom lambda function
|
113
|
+
def add_lambda(handler:, env: {}, settings: {})
|
114
|
+
lambda_resource = generate_lambda_resource(
|
115
|
+
description: "Lambda for #{handler}",
|
116
|
+
function_name: ([app_name] << handler.split('.')).flatten.join('-').dasherize,
|
117
|
+
handler: handler,
|
118
|
+
s3_key: lambda_handler_s3_key,
|
119
|
+
env_vars: env.merge('app_env' => Humidifier.ref('AppEnvironment')),
|
120
|
+
role: Humidifier.fn.get_att(['LambdaRole', 'Arn']),
|
121
|
+
settings: settings
|
122
|
+
)
|
123
|
+
stack.add(handler.gsub('.', '_').camelize, lambda_resource)
|
124
|
+
end
|
125
|
+
|
126
|
+
# generic lambda function for gateway
|
127
|
+
def add_generic_lambda(gateway: {}, mod: {}, wrapper: {}, env: {}, settings: {})
|
128
|
+
lambda_config = {}
|
129
|
+
name_parts = mod[:name].split('::')
|
130
|
+
{gateway: gateway, module: mod, wrapper: wrapper}.each do |env_group_prefix, env_group|
|
131
|
+
env_group.each{|env_key, env_value| lambda_config["#{env_group_prefix}_#{env_key}"] = env_value}
|
132
|
+
end
|
133
|
+
env_vars = env
|
134
|
+
.reduce({}){|env_as_string, (k, v)| env_as_string.update(k.to_s => v.to_s)}
|
135
|
+
.merge(lambda_config)
|
136
|
+
.merge(
|
137
|
+
'GEM_PATH' => GEM_PATH,
|
138
|
+
'app_dir' => app_dir,
|
139
|
+
'app_env' => Humidifier.ref('AppEnvironment')
|
140
|
+
)
|
141
|
+
|
142
|
+
lambda_resource = generate_lambda_resource(
|
143
|
+
description: "Lambda for #{mod[:name]}.#{mod[:method]}",
|
144
|
+
function_name: [app_name, name_parts, mod[:method]].flatten.join('-').dasherize,
|
145
|
+
handler: "#{LAMBDA_HANDLER_FILE_NAME}.AwsLambdaHandler.call",
|
146
|
+
s3_key: LAMBDA_HANDLER_FILE_NAME + '.rb.zip',
|
147
|
+
env_vars: env_vars,
|
148
|
+
role: Humidifier.fn.get_att(['LambdaRole', 'Arn']),
|
149
|
+
settings: settings,
|
150
|
+
layers: [Humidifier.ref(app_name + 'Layer'), Humidifier.ref(app_name + 'GemsLayer')]
|
151
|
+
)
|
152
|
+
|
153
|
+
# add to stack
|
154
|
+
['Lambda', name_parts, mod[:method].capitalize].join.tap do |id|
|
155
|
+
stack.add(id, lambda_resource)
|
156
|
+
stack.add_lambda_invoke_permission(id: id, gateway: gateway)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def generate_lambda_resource(description:, function_name:, handler:, s3_key:, env_vars:, role:, settings:, layers: [])
|
161
|
+
lambda_function = Humidifier::Lambda::Function.new(
|
162
|
+
description: description,
|
163
|
+
function_name: function_name,
|
164
|
+
handler: handler,
|
165
|
+
environment: {variables: env_vars},
|
166
|
+
role: role,
|
167
|
+
timeout: settings[:timeout] || stack_opts[:timeout] || 15,
|
168
|
+
memory_size: settings[:memory_size] || stack_opts[:memory_size] || 128,
|
169
|
+
runtime: RUBY_VERSION,
|
170
|
+
code: {
|
171
|
+
s3_bucket: s3_bucket,
|
172
|
+
s3_key: s3_key,
|
173
|
+
s3_object_version: lambda_handler_s3_object_version
|
174
|
+
},
|
175
|
+
layers: layers
|
176
|
+
)
|
177
|
+
end
|
178
|
+
|
179
|
+
# invoke permission
|
180
|
+
def add_lambda_invoke_permission(id:, gateway:)
|
181
|
+
arn_path_matcher = gateway[:path].split('/').each_with_object([]) do |fragment, matcher|
|
182
|
+
fragment = '*' if fragment.start_with?(':')
|
183
|
+
matcher << fragment
|
184
|
+
end.join('/')
|
185
|
+
stack.add(id + 'InvokePermission' , Humidifier::Lambda::Permission.new(
|
186
|
+
action: "lambda:InvokeFunction",
|
187
|
+
function_name: Humidifier.fn.get_att([id, 'Arn']),
|
188
|
+
principal: "apigateway.amazonaws.com",
|
189
|
+
source_arn: Humidifier.fn.sub("arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${#{api_gateway_id}}/*/#{gateway[:verb]}/#{arn_path_matcher}")
|
190
|
+
)
|
191
|
+
)
|
192
|
+
end
|
193
|
+
|
194
|
+
# gateway method
|
195
|
+
def add_api_gateway_resources(gateway:, lambda:)
|
196
|
+
|
197
|
+
# example: calculator/algebra/:x/:y/sum -> module name, args, method name
|
198
|
+
path = gateway[:path].split('/')
|
199
|
+
|
200
|
+
# root resource
|
201
|
+
root_resource = path.shift
|
202
|
+
stack.add(root_resource.camelize, Humidifier::ApiGateway::Resource.new(
|
203
|
+
rest_api_id: Humidifier.ref(api_gateway_id),
|
204
|
+
parent_id: Humidifier.fn.get_att(["ApiGateway", "RootResourceId"]),
|
205
|
+
path_part: root_resource
|
206
|
+
)
|
207
|
+
)
|
208
|
+
|
209
|
+
# args and method name are nested resources
|
210
|
+
parent_resource = root_resource.camelize
|
211
|
+
path.each do |fragment|
|
212
|
+
if fragment.start_with?(':')
|
213
|
+
fragment = fragment[1..-1]
|
214
|
+
dynamic_fragment = "{#{fragment}}"
|
215
|
+
end
|
216
|
+
stack.add(parent_resource + fragment.camelize, Humidifier::ApiGateway::Resource.new(
|
217
|
+
rest_api_id: Humidifier.ref(api_gateway_id),
|
218
|
+
parent_id: Humidifier.ref(parent_resource),
|
219
|
+
path_part: dynamic_fragment || fragment
|
220
|
+
)
|
221
|
+
)
|
222
|
+
parent_resource = parent_resource + fragment.camelize
|
223
|
+
end
|
224
|
+
|
225
|
+
# attach lambda to last resource
|
226
|
+
id = 'EndpointFor' + (gateway[:path].gsub(':', '').gsub('/', '_')).camelize
|
227
|
+
stack.add(id, Humidifier::ApiGateway::Method.new(
|
228
|
+
authorization_type: 'NONE',
|
229
|
+
http_method: gateway[:verb].to_s.upcase,
|
230
|
+
integration: {
|
231
|
+
integration_http_method: 'POST',
|
232
|
+
type: "AWS_PROXY",
|
233
|
+
uri: Humidifier.fn.sub([
|
234
|
+
"arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${lambdaArn}/invocations",
|
235
|
+
'lambdaArn' => Humidifier.fn.get_att([lambda, 'Arn'])
|
236
|
+
])
|
237
|
+
},
|
238
|
+
rest_api_id: Humidifier.ref(api_gateway_id),
|
239
|
+
resource_id: Humidifier.ref(parent_resource) # last evaluated resource
|
240
|
+
)
|
241
|
+
)
|
242
|
+
|
243
|
+
# deployment depends on each endpoint
|
244
|
+
api_gateway_deployment.depends_on << id
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
# delegate from stack instance to our module
|
249
|
+
module Humidifier
|
250
|
+
class Stack
|
251
|
+
extend Forwardable
|
252
|
+
[StackBuilder, StackBuilder::LambdaPolicy].each do |mod|
|
253
|
+
def_delegators mod.to_s.to_sym, *mod.singleton_methods
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|