cloud-mu 3.2.0 → 3.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Dockerfile +1 -1
- data/ansible/roles/mu-nat/tasks/main.yml +3 -0
- data/bin/mu-adopt +12 -1
- data/bin/mu-aws-setup +41 -7
- data/bin/mu-azure-setup +34 -0
- data/bin/mu-configure +214 -119
- data/bin/mu-gcp-setup +37 -2
- data/bin/mu-load-config.rb +2 -1
- data/bin/mu-node-manage +3 -0
- data/bin/mu-refresh-ssl +67 -0
- data/bin/mu-run-tests +28 -6
- data/bin/mu-self-update +30 -10
- data/bin/mu-upload-chef-artifacts +30 -26
- data/cloud-mu.gemspec +10 -8
- data/cookbooks/mu-master/attributes/default.rb +5 -1
- data/cookbooks/mu-master/metadata.rb +2 -2
- data/cookbooks/mu-master/recipes/default.rb +81 -26
- data/cookbooks/mu-master/recipes/init.rb +197 -62
- data/cookbooks/mu-master/recipes/update_nagios_only.rb +1 -1
- data/cookbooks/mu-master/recipes/vault.rb +78 -77
- data/cookbooks/mu-master/templates/default/mods/rewrite.conf.erb +1 -0
- data/cookbooks/mu-master/templates/default/nagios.conf.erb +103 -0
- data/cookbooks/mu-master/templates/default/web_app.conf.erb +14 -30
- data/cookbooks/mu-tools/attributes/default.rb +12 -0
- data/cookbooks/mu-tools/files/centos-6/CentOS-Base.repo +47 -0
- data/cookbooks/mu-tools/libraries/helper.rb +98 -4
- data/cookbooks/mu-tools/libraries/monkey.rb +1 -1
- data/cookbooks/mu-tools/recipes/apply_security.rb +31 -9
- data/cookbooks/mu-tools/recipes/aws_api.rb +8 -2
- data/cookbooks/mu-tools/recipes/base_repositories.rb +1 -1
- data/cookbooks/mu-tools/recipes/gcloud.rb +2 -9
- data/cookbooks/mu-tools/recipes/google_api.rb +7 -0
- data/cookbooks/mu-tools/recipes/rsyslog.rb +8 -1
- data/cookbooks/mu-tools/resources/disk.rb +113 -42
- data/cookbooks/mu-tools/resources/mommacat_request.rb +1 -2
- data/cookbooks/mu-tools/templates/centos-8/sshd_config.erb +215 -0
- data/extras/Gemfile.lock.bootstrap +394 -0
- data/extras/bucketstubs/error.html +0 -0
- data/extras/bucketstubs/index.html +0 -0
- data/extras/clean-stock-amis +11 -3
- data/extras/generate-stock-images +6 -3
- data/extras/git_rpm/build.sh +20 -0
- data/extras/git_rpm/mugit.spec +53 -0
- data/extras/image-generators/AWS/centos7.yaml +19 -16
- data/extras/image-generators/AWS/{rhel7.yaml → rhel71.yaml} +0 -0
- data/extras/image-generators/AWS/{win2k12.yaml → win2k12r2.yaml} +0 -0
- data/extras/image-generators/VMWare/centos8.yaml +15 -0
- data/extras/openssl_rpm/build.sh +19 -0
- data/extras/openssl_rpm/mussl.spec +46 -0
- data/extras/python_rpm/muthon.spec +14 -4
- data/extras/ruby_rpm/muby.spec +9 -5
- data/extras/sqlite_rpm/build.sh +19 -0
- data/extras/sqlite_rpm/muqlite.spec +47 -0
- data/install/installer +7 -5
- data/modules/mommacat.ru +2 -2
- data/modules/mu.rb +14 -7
- data/modules/mu/adoption.rb +5 -5
- data/modules/mu/cleanup.rb +47 -25
- data/modules/mu/cloud.rb +29 -1
- data/modules/mu/cloud/dnszone.rb +0 -2
- data/modules/mu/cloud/machine_images.rb +1 -1
- data/modules/mu/cloud/providers.rb +6 -1
- data/modules/mu/cloud/resource_base.rb +16 -7
- data/modules/mu/cloud/ssh_sessions.rb +5 -1
- data/modules/mu/cloud/wrappers.rb +20 -7
- data/modules/mu/config.rb +28 -12
- data/modules/mu/config/bucket.rb +31 -2
- data/modules/mu/config/cache_cluster.rb +1 -1
- data/modules/mu/config/cdn.rb +100 -0
- data/modules/mu/config/container_cluster.rb +1 -1
- data/modules/mu/config/database.rb +3 -3
- data/modules/mu/config/dnszone.rb +4 -3
- data/modules/mu/config/endpoint.rb +1 -0
- data/modules/mu/config/firewall_rule.rb +1 -1
- data/modules/mu/config/function.rb +16 -7
- data/modules/mu/config/job.rb +89 -0
- data/modules/mu/config/notifier.rb +7 -18
- data/modules/mu/config/ref.rb +55 -9
- data/modules/mu/config/schema_helpers.rb +12 -3
- data/modules/mu/config/server.rb +11 -5
- data/modules/mu/config/server_pool.rb +2 -2
- data/modules/mu/config/vpc.rb +11 -10
- data/modules/mu/defaults/AWS.yaml +106 -106
- data/modules/mu/deploy.rb +40 -14
- data/modules/mu/groomers/chef.rb +2 -2
- data/modules/mu/master.rb +70 -3
- data/modules/mu/mommacat.rb +28 -9
- data/modules/mu/mommacat/daemon.rb +13 -7
- data/modules/mu/mommacat/naming.rb +2 -2
- data/modules/mu/mommacat/search.rb +16 -5
- data/modules/mu/mommacat/storage.rb +67 -32
- data/modules/mu/providers/aws.rb +298 -85
- data/modules/mu/providers/aws/alarm.rb +5 -5
- data/modules/mu/providers/aws/bucket.rb +284 -50
- data/modules/mu/providers/aws/cache_cluster.rb +26 -26
- data/modules/mu/providers/aws/cdn.rb +782 -0
- data/modules/mu/providers/aws/collection.rb +16 -16
- data/modules/mu/providers/aws/container_cluster.rb +84 -64
- data/modules/mu/providers/aws/database.rb +59 -55
- data/modules/mu/providers/aws/dnszone.rb +29 -12
- data/modules/mu/providers/aws/endpoint.rb +535 -50
- data/modules/mu/providers/aws/firewall_rule.rb +32 -26
- data/modules/mu/providers/aws/folder.rb +1 -1
- data/modules/mu/providers/aws/function.rb +300 -134
- data/modules/mu/providers/aws/group.rb +16 -14
- data/modules/mu/providers/aws/habitat.rb +4 -4
- data/modules/mu/providers/aws/job.rb +469 -0
- data/modules/mu/providers/aws/loadbalancer.rb +67 -45
- data/modules/mu/providers/aws/log.rb +17 -17
- data/modules/mu/providers/aws/msg_queue.rb +22 -13
- data/modules/mu/providers/aws/nosqldb.rb +99 -8
- data/modules/mu/providers/aws/notifier.rb +137 -65
- data/modules/mu/providers/aws/role.rb +119 -83
- data/modules/mu/providers/aws/search_domain.rb +166 -30
- data/modules/mu/providers/aws/server.rb +209 -118
- data/modules/mu/providers/aws/server_pool.rb +95 -130
- data/modules/mu/providers/aws/storage_pool.rb +19 -11
- data/modules/mu/providers/aws/user.rb +5 -5
- data/modules/mu/providers/aws/userdata/linux.erb +5 -4
- data/modules/mu/providers/aws/vpc.rb +109 -54
- data/modules/mu/providers/aws/vpc_subnet.rb +43 -39
- data/modules/mu/providers/azure.rb +78 -12
- data/modules/mu/providers/azure/server.rb +20 -4
- data/modules/mu/providers/cloudformation/server.rb +1 -1
- data/modules/mu/providers/google.rb +21 -5
- data/modules/mu/providers/google/bucket.rb +1 -1
- data/modules/mu/providers/google/container_cluster.rb +1 -1
- data/modules/mu/providers/google/database.rb +1 -1
- data/modules/mu/providers/google/firewall_rule.rb +1 -1
- data/modules/mu/providers/google/folder.rb +7 -3
- data/modules/mu/providers/google/function.rb +66 -31
- data/modules/mu/providers/google/group.rb +1 -1
- data/modules/mu/providers/google/habitat.rb +1 -1
- data/modules/mu/providers/google/loadbalancer.rb +1 -1
- data/modules/mu/providers/google/role.rb +6 -3
- data/modules/mu/providers/google/server.rb +1 -1
- data/modules/mu/providers/google/server_pool.rb +1 -1
- data/modules/mu/providers/google/user.rb +1 -1
- data/modules/mu/providers/google/vpc.rb +28 -3
- data/modules/tests/aws-jobs-functions.yaml +46 -0
- data/modules/tests/aws-servers-with-handrolled-iam.yaml +37 -0
- data/modules/tests/centos6.yaml +4 -0
- data/modules/tests/centos7.yaml +4 -0
- data/modules/tests/ecs.yaml +2 -2
- data/modules/tests/eks.yaml +1 -1
- data/modules/tests/functions/node-function/lambda_function.js +10 -0
- data/modules/tests/functions/python-function/lambda_function.py +12 -0
- data/modules/tests/k8s.yaml +1 -1
- data/modules/tests/microservice_app.yaml +288 -0
- data/modules/tests/rds.yaml +5 -5
- data/modules/tests/regrooms/rds.yaml +5 -5
- data/modules/tests/server-with-scrub-muisms.yaml +1 -1
- data/modules/tests/super_complex_bok.yml +2 -2
- data/modules/tests/super_simple_bok.yml +2 -2
- metadata +42 -17
|
@@ -13,22 +13,21 @@ module MU
|
|
|
13
13
|
|
|
14
14
|
# Called automatically by {MU::Deploy#createResources}
|
|
15
15
|
def create
|
|
16
|
-
resp = MU::Cloud::AWS.apig(region: @
|
|
16
|
+
resp = MU::Cloud::AWS.apig(region: @region, credentials: @credentials).create_rest_api(
|
|
17
17
|
name: @mu_name,
|
|
18
18
|
description: @deploy.deploy_id,
|
|
19
19
|
endpoint_configuration: {
|
|
20
20
|
types: ["REGIONAL"] # XXX expose in BoK ["REGIONAL", "EDGE", "PRIVATE"]
|
|
21
|
-
}
|
|
21
|
+
},
|
|
22
|
+
tags: @tags
|
|
22
23
|
)
|
|
23
24
|
@cloud_id = resp.id
|
|
24
|
-
generate_methods
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
generate_methods(false)
|
|
27
26
|
end
|
|
28
27
|
|
|
29
28
|
# Create/update all of the methods declared for this endpoint
|
|
30
|
-
def generate_methods
|
|
31
|
-
resp = MU::Cloud::AWS.apig(region: @
|
|
29
|
+
def generate_methods(integrations = true)
|
|
30
|
+
resp = MU::Cloud::AWS.apig(region: @region, credentials: @credentials).get_resources(
|
|
32
31
|
rest_api_id: @cloud_id,
|
|
33
32
|
)
|
|
34
33
|
root_resource = resp.items.first.id
|
|
@@ -37,24 +36,26 @@ module MU
|
|
|
37
36
|
@config['methods'].each { |m|
|
|
38
37
|
m["auth"] ||= m["iam_role"] ? "AWS_IAM" : "NONE"
|
|
39
38
|
|
|
40
|
-
method_arn = "arn:#{MU::Cloud::AWS.isGovCloud?(@
|
|
39
|
+
method_arn = "arn:#{MU::Cloud::AWS.isGovCloud?(@region) ? "aws-us-gov" : "aws"}:execute-api:#{@region}:#{MU::Cloud::AWS.credToAcct(@credentials)}:#{@cloud_id}/*/#{m['type']}/#{m['path']}"
|
|
40
|
+
path_part = ["", "/"].include?(m['path']) ? nil : m['path']
|
|
41
|
+
method_arn.sub!(/\/\/$/, '/')
|
|
41
42
|
|
|
42
|
-
resp = MU::Cloud::AWS.apig(region: @
|
|
43
|
+
resp = MU::Cloud::AWS.apig(region: @region, credentials: @credentials).get_resources(
|
|
43
44
|
rest_api_id: @cloud_id
|
|
44
45
|
)
|
|
45
46
|
ext_resource = nil
|
|
46
47
|
resp.items.each { |resource|
|
|
47
|
-
if resource.path_part ==
|
|
48
|
+
if resource.path_part == path_part
|
|
48
49
|
ext_resource = resource.id
|
|
49
50
|
end
|
|
50
51
|
}
|
|
51
52
|
|
|
52
53
|
resp = if ext_resource
|
|
53
|
-
MU::Cloud::AWS.apig(region: @
|
|
54
|
+
MU::Cloud::AWS.apig(region: @region, credentials: @credentials).get_resource(
|
|
54
55
|
rest_api_id: @cloud_id,
|
|
55
56
|
resource_id: ext_resource,
|
|
56
57
|
)
|
|
57
|
-
# MU::Cloud::AWS.apig(region: @
|
|
58
|
+
# MU::Cloud::AWS.apig(region: @region, credentials: @credentials).update_resource(
|
|
58
59
|
# rest_api_id: @cloud_id,
|
|
59
60
|
# resource_id: ext_resource,
|
|
60
61
|
# patch_operations: [
|
|
@@ -66,22 +67,22 @@ MU::Cloud::AWS.apig(region: @config['region'], credentials: @config['credentials
|
|
|
66
67
|
# ]
|
|
67
68
|
# )
|
|
68
69
|
else
|
|
69
|
-
MU::Cloud::AWS.apig(region: @
|
|
70
|
+
MU::Cloud::AWS.apig(region: @region, credentials: @credentials).create_resource(
|
|
70
71
|
rest_api_id: @cloud_id,
|
|
71
72
|
parent_id: root_resource,
|
|
72
|
-
path_part:
|
|
73
|
+
path_part: path_part
|
|
73
74
|
)
|
|
74
75
|
end
|
|
75
76
|
parent_id = resp.id
|
|
76
77
|
|
|
77
78
|
resp = begin
|
|
78
|
-
MU::Cloud::AWS.apig(region: @
|
|
79
|
+
MU::Cloud::AWS.apig(region: @region, credentials: @credentials).get_method(
|
|
79
80
|
rest_api_id: @cloud_id,
|
|
80
81
|
resource_id: parent_id,
|
|
81
82
|
http_method: m['type']
|
|
82
83
|
)
|
|
83
84
|
rescue Aws::APIGateway::Errors::NotFoundException
|
|
84
|
-
resp = MU::Cloud::AWS.apig(region: @
|
|
85
|
+
resp = MU::Cloud::AWS.apig(region: @region, credentials: @credentials).put_method(
|
|
85
86
|
rest_api_id: @cloud_id,
|
|
86
87
|
resource_id: parent_id,
|
|
87
88
|
authorization_type: m['auth'],
|
|
@@ -100,6 +101,7 @@ MU::Cloud::AWS.apig(region: @config['region'], credentials: @config['credentials
|
|
|
100
101
|
}
|
|
101
102
|
if r['headers']
|
|
102
103
|
params[:response_parameters] = r['headers'].map { |h|
|
|
104
|
+
h['required'] ||= false
|
|
103
105
|
["method.response.header."+h['header'], h['required']]
|
|
104
106
|
}.to_h
|
|
105
107
|
end
|
|
@@ -109,13 +111,13 @@ MU::Cloud::AWS.apig(region: @config['region'], credentials: @config['credentials
|
|
|
109
111
|
params[:response_models] = r['body'].map { |b| [b['content_type'], b['is_error'] ? "Error" : "Empty"] }.to_h
|
|
110
112
|
end
|
|
111
113
|
|
|
112
|
-
MU::Cloud::AWS.apig(region: @
|
|
114
|
+
MU::Cloud::AWS.apig(region: @region, credentials: @credentials).put_method_response(params)
|
|
113
115
|
}
|
|
114
116
|
rescue Aws::APIGateway::Errors::ConflictException
|
|
115
117
|
# fine to ignore
|
|
116
118
|
end
|
|
117
119
|
|
|
118
|
-
if m['integrate_with']
|
|
120
|
+
if integrations and m['integrate_with']
|
|
119
121
|
# role_arn = if m['iam_role']
|
|
120
122
|
# if m['iam_role'].match(/^arn:/)
|
|
121
123
|
# m['iam_role']
|
|
@@ -127,13 +129,17 @@ MU::Cloud::AWS.apig(region: @config['region'], credentials: @config['credentials
|
|
|
127
129
|
# end
|
|
128
130
|
|
|
129
131
|
function_obj = nil
|
|
132
|
+
aws_int_type = m['integrate_with']['proxy'] ? "AWS_PROXY" : "AWS"
|
|
130
133
|
|
|
131
134
|
uri, type = if m['integrate_with']['type'] == "aws_generic"
|
|
132
135
|
svc, action = m['integrate_with']['aws_generic_action'].split(/:/)
|
|
133
|
-
["arn:aws:apigateway:"+@
|
|
134
|
-
elsif m['integrate_with']['type'] == "
|
|
135
|
-
function_obj =
|
|
136
|
-
[
|
|
136
|
+
["arn:aws:apigateway:"+@region+":#{svc}:action/#{action}", aws_int_type]
|
|
137
|
+
elsif m['integrate_with']['type'] == "functions"
|
|
138
|
+
function_obj = nil
|
|
139
|
+
MU.retrier([], max: 5, wait: 9, loop_if: Proc.new { function_obj.nil? }) {
|
|
140
|
+
function_obj = @deploy.findLitterMate(name: m['integrate_with']['name'], type: "functions")
|
|
141
|
+
}
|
|
142
|
+
["arn:aws:apigateway:"+@region+":lambda:path/2015-03-31/functions/"+function_obj.cloudobj.arn+"/invocations", aws_int_type]
|
|
137
143
|
elsif m['integrate_with']['type'] == "mock"
|
|
138
144
|
[nil, "MOCK"]
|
|
139
145
|
end
|
|
@@ -143,7 +149,8 @@ MU::Cloud::AWS.apig(region: @config['region'], credentials: @config['credentials
|
|
|
143
149
|
:resource_id => parent_id,
|
|
144
150
|
:type => type, # XXX Lambda and Firehose can do AWS_PROXY
|
|
145
151
|
:content_handling => "CONVERT_TO_TEXT", # XXX expose in BoK
|
|
146
|
-
:http_method => m['type']
|
|
152
|
+
:http_method => m['type'],
|
|
153
|
+
:timeout_in_millis => m['timeout_in_millis']
|
|
147
154
|
# credentials: role_arn
|
|
148
155
|
}
|
|
149
156
|
params[:uri] = uri if uri
|
|
@@ -163,10 +170,15 @@ MU::Cloud::AWS.apig(region: @config['region'], credentials: @config['credentials
|
|
|
163
170
|
params[:request_templates][rt['content_type']] = rt['template']
|
|
164
171
|
}
|
|
165
172
|
end
|
|
173
|
+
if m['integrate_with']['parameters']
|
|
174
|
+
params[:request_parameters] = Hash[m['integrate_with']['parameters'].map { |p|
|
|
175
|
+
["integration.request.#{p['type']}.#{p['name']}", p['value']]
|
|
176
|
+
}]
|
|
177
|
+
end
|
|
166
178
|
|
|
167
|
-
resp = MU::Cloud::AWS.apig(region: @
|
|
179
|
+
resp = MU::Cloud::AWS.apig(region: @region, credentials: @credentials).put_integration(params)
|
|
168
180
|
|
|
169
|
-
if m['integrate_with']['type']
|
|
181
|
+
if m['integrate_with']['type'] =~ /^functions?$/
|
|
170
182
|
function_obj.addTrigger(method_arn, "apigateway", @config['name'])
|
|
171
183
|
end
|
|
172
184
|
|
|
@@ -176,7 +188,7 @@ MU::Cloud::AWS.apig(region: @config['region'], credentials: @config['credentials
|
|
|
176
188
|
:resource_id => parent_id,
|
|
177
189
|
:http_method => m['type'],
|
|
178
190
|
:status_code => r['code'].to_s,
|
|
179
|
-
:selection_pattern => ""
|
|
191
|
+
:selection_pattern => ".*"
|
|
180
192
|
}
|
|
181
193
|
if r['headers']
|
|
182
194
|
params[:response_parameters] = r['headers'].map { |h|
|
|
@@ -184,7 +196,7 @@ MU::Cloud::AWS.apig(region: @config['region'], credentials: @config['credentials
|
|
|
184
196
|
}.to_h
|
|
185
197
|
end
|
|
186
198
|
|
|
187
|
-
MU::Cloud::AWS.apig(region: @
|
|
199
|
+
MU::Cloud::AWS.apig(region: @region, credentials: @credentials).put_integration_response(params)
|
|
188
200
|
|
|
189
201
|
}
|
|
190
202
|
|
|
@@ -197,24 +209,152 @@ MU::Cloud::AWS.apig(region: @config['region'], credentials: @config['credentials
|
|
|
197
209
|
def groom
|
|
198
210
|
generate_methods
|
|
199
211
|
|
|
200
|
-
MU.
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
212
|
+
deployment = MU::Cloud::AWS.apig(region: @region, credentials: @credentials).get_deployments(
|
|
213
|
+
rest_api_id: @cloud_id
|
|
214
|
+
).items.sort { |a, b| a.created_date <=> b.created_date }.last
|
|
215
|
+
|
|
216
|
+
if !deployment
|
|
217
|
+
MU.log "Deploying API Gateway #{@config['name']} to #{@config['deploy_to']}"
|
|
218
|
+
deployment = MU::Cloud::AWS.apig(region: @region, credentials: @credentials).create_deployment(
|
|
219
|
+
rest_api_id: @cloud_id,
|
|
220
|
+
stage_name: @config['deploy_to']
|
|
204
221
|
# cache_cluster_enabled: false,
|
|
205
222
|
# cache_cluster_size: 0.5,
|
|
206
|
-
|
|
223
|
+
)
|
|
224
|
+
end
|
|
207
225
|
# this automatically creates a stage with the same name, so we don't
|
|
208
226
|
# have to deal with that
|
|
209
227
|
|
|
210
|
-
|
|
228
|
+
my_hostname = @cloud_id+".execute-api."+@region+".amazonaws.com"
|
|
229
|
+
my_url = "https://"+my_hostname+"/"+@config['deploy_to']
|
|
211
230
|
MU.log "API Endpoint #{@config['name']}: "+my_url, MU::SUMMARY
|
|
212
231
|
|
|
213
|
-
|
|
232
|
+
print_dns_alias = Proc.new { |rec|
|
|
233
|
+
rec['name'] ||= @mu_name.downcase
|
|
234
|
+
dnsname = MU::Cloud.resourceClass("AWS", "DNSZone").recordToName(rec)
|
|
235
|
+
dnsname
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
# if we have any placeholder DNS records that are intended to be
|
|
239
|
+
# filled out with our runtime @mu_name, do so, and add an alias if
|
|
240
|
+
# applicable
|
|
241
|
+
if @config['dns_records'] and !MU::Cloud::AWS.isGovCloud?
|
|
242
|
+
@config['dns_records'].each { |rec|
|
|
243
|
+
dnsname = print_dns_alias.call(rec)
|
|
244
|
+
MU.log "Alias for API Endpoint #{@config['name']}: https://"+dnsname+"/"+@config['deploy_to'], MU::SUMMARY
|
|
245
|
+
}
|
|
246
|
+
MU::Cloud.resourceClass("AWS", "DNSZone").createRecordsFromConfig(@config['dns_records'], target: my_hostname)
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
if @config['domain_names']
|
|
250
|
+
@config['domain_names'].each { |dom|
|
|
251
|
+
dnsname = if dom['dns_record']
|
|
252
|
+
print_dns_alias.call(dom['dns_record'])
|
|
253
|
+
else
|
|
254
|
+
dom['unmanaged_name']
|
|
255
|
+
end
|
|
256
|
+
MU.log "Alias for API Endpoint #{@config['name']}: https://"+dnsname, MU::SUMMARY
|
|
257
|
+
|
|
258
|
+
certfield, dnsfield = if dom['endpoint_type'] == "EDGE"
|
|
259
|
+
[:certificate_arn, :distribution_domain_name]
|
|
260
|
+
else
|
|
261
|
+
[:regional_certificate_arn, :regional_domain_name]
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
dom_desc = begin
|
|
265
|
+
MU::Cloud::AWS.apig(region: @region, credentials: @credentials).get_domain_name(domain_name: dnsname)
|
|
266
|
+
rescue ::Aws::APIGateway::Errors::NotFoundException
|
|
267
|
+
|
|
268
|
+
params = {
|
|
269
|
+
domain_name: dnsname,
|
|
270
|
+
endpoint_configuration: {
|
|
271
|
+
types: [dom['endpoint_type']]
|
|
272
|
+
},
|
|
273
|
+
security_policy: dom['security_policy'],
|
|
274
|
+
tags: @tags
|
|
275
|
+
}
|
|
276
|
+
if dom['certificate']
|
|
277
|
+
params[certfield] = dom['certificate']['id']
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
MU.log "Creating API Gateway Domain Name #{dnsname}", MU::NOTICE, details: params
|
|
281
|
+
MU::Cloud::AWS.apig(region: @region, credentials: @credentials).create_domain_name(params)
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
mappings = MU::Cloud::AWS.apig(region: @region, credentials: @credentials).get_base_path_mappings(domain_name: dnsname, limit: 500).items
|
|
285
|
+
found = false
|
|
286
|
+
if mappings
|
|
287
|
+
mappings.each { |m|
|
|
288
|
+
if m.rest_api_id == @cloud_id and m.stage == @config['deploy_to']
|
|
289
|
+
found = true
|
|
290
|
+
break
|
|
291
|
+
end
|
|
292
|
+
}
|
|
293
|
+
end
|
|
294
|
+
if !found
|
|
295
|
+
MU.log "Mapping #{dnsname} to API Gateway #{@mu_name}"
|
|
296
|
+
MU::Cloud::AWS.apig(region: @region, credentials: @credentials).create_base_path_mapping(
|
|
297
|
+
domain_name: dnsname,
|
|
298
|
+
rest_api_id: @cloud_id,
|
|
299
|
+
stage: @config['deploy_to']
|
|
300
|
+
)
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
if dom['dns_record']
|
|
304
|
+
MU::Cloud.resourceClass("AWS", "DNSZone").createRecordsFromConfig([dom['dns_record']], target: dom_desc.send(dnsfield))
|
|
305
|
+
end
|
|
306
|
+
}
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
# The creation of our deployment should have created a matching stage,
|
|
310
|
+
# which we're now going to mess with.
|
|
311
|
+
stage = MU::Cloud::AWS.apig(region: @region, credentials: @credentials).get_stage(
|
|
312
|
+
rest_api_id: @cloud_id,
|
|
313
|
+
stage_name: @config['deploy_to']
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
if @config['access_logs'] and !stage.access_log_settings
|
|
317
|
+
log_ref = MU::Config::Ref.get(@config['access_logs'])
|
|
318
|
+
MU.log "Enabling API Gateway access logs to CloudWatch Log Group #{log_ref.cloud_id}"
|
|
319
|
+
stage = MU::Cloud::AWS.apig(region: @region, credentials: @credentials).update_stage(
|
|
320
|
+
rest_api_id: @cloud_id,
|
|
321
|
+
stage_name: @config['deploy_to'],
|
|
322
|
+
patch_operations: [
|
|
323
|
+
{
|
|
324
|
+
op: "replace",
|
|
325
|
+
path: "/accessLogSettings/destinationArn",
|
|
326
|
+
value: log_ref.kitten.arn.sub(/:\*$/, '')
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
op: "replace",
|
|
330
|
+
path: "/accessLogSettings/format",
|
|
331
|
+
value: '$context.identity.sourceIp $context.identity.caller $context.identity.user [$context.requestTime] "$context.httpMethod $context.resourcePath $context.protocol" $context.status $context.responseLength $context.requestId'
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
op: "replace",
|
|
335
|
+
path: "/description",
|
|
336
|
+
value: @deploy.deploy_id
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
op: "replace",
|
|
340
|
+
path: "/*/*/logging/dataTrace",
|
|
341
|
+
value: "true"
|
|
342
|
+
},
|
|
343
|
+
{
|
|
344
|
+
op: "replace",
|
|
345
|
+
path: "/*/*/logging/loglevel",
|
|
346
|
+
value: "INFO"
|
|
347
|
+
}
|
|
348
|
+
]
|
|
349
|
+
)
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
# resp = MU::Cloud::AWS.apig(region: @region, credentials: @credentials).create_authorizer(
|
|
214
354
|
# rest_api_id: @cloud_id,
|
|
215
355
|
# )
|
|
216
356
|
|
|
217
|
-
# resp = MU::Cloud::AWS.apig(region: @
|
|
357
|
+
# resp = MU::Cloud::AWS.apig(region: @region, credentials: @credentials).create_vpc_link(
|
|
218
358
|
# )
|
|
219
359
|
|
|
220
360
|
end
|
|
@@ -223,7 +363,8 @@ MU::Cloud::AWS.apig(region: @config['region'], credentials: @config['credentials
|
|
|
223
363
|
# @return [Struct]
|
|
224
364
|
def cloud_desc(use_cache: true)
|
|
225
365
|
return @cloud_desc_cache if @cloud_desc_cache and use_cache
|
|
226
|
-
|
|
366
|
+
return nil if !@cloud_id
|
|
367
|
+
@cloud_desc_cache = MU::Cloud::AWS.apig(region: @region, credentials: @credentials).get_rest_api(
|
|
227
368
|
rest_api_id: @cloud_id
|
|
228
369
|
)
|
|
229
370
|
@cloud_desc_cache
|
|
@@ -232,7 +373,10 @@ MU::Cloud::AWS.apig(region: @config['region'], credentials: @config['credentials
|
|
|
232
373
|
# Return the metadata for this API
|
|
233
374
|
# @return [Hash]
|
|
234
375
|
def notify
|
|
235
|
-
|
|
376
|
+
return nil if !@cloud_id or !cloud_desc(use_cache: false)
|
|
377
|
+
deploy_struct = MU.structToHash(cloud_desc, stringify_keys: true)
|
|
378
|
+
deploy_struct['url'] = "https://"+@cloud_id+".execute-api."+@region+".amazonaws.com"
|
|
379
|
+
deploy_struct['url'] += "/"+@config['deploy_to'] if @config['deploy_to']
|
|
236
380
|
# XXX stages and whatnot
|
|
237
381
|
return deploy_struct
|
|
238
382
|
end
|
|
@@ -242,15 +386,45 @@ MU::Cloud::AWS.apig(region: @config['region'], credentials: @config['credentials
|
|
|
242
386
|
# @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
|
|
243
387
|
# @param region [String]: The cloud provider region
|
|
244
388
|
# @return [void]
|
|
245
|
-
def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
|
|
389
|
+
def self.cleanup(noop: false, deploy_id: MU.deploy_id, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
|
|
246
390
|
MU.log "AWS::Endpoint.cleanup: need to support flags['known']", MU::DEBUG, details: flags
|
|
247
391
|
MU.log "Placeholder: AWS Endpoint artifacts do not support tags, so ignoremaster cleanup flag has no effect", MU::DEBUG, details: ignoremaster
|
|
248
392
|
|
|
393
|
+
resp = MU::Cloud::AWS.apig(region: region, credentials: credentials).get_domain_names(limit: 500)
|
|
394
|
+
if resp and resp.items
|
|
395
|
+
resp.items.each { |d|
|
|
396
|
+
next if !d.tags
|
|
397
|
+
if d.tags["MU-ID"] == deploy_id and
|
|
398
|
+
(ignoremaster or d.tags["MU-MASTER-IP"] == MU.mu_public_ip)
|
|
399
|
+
mappings = MU::Cloud::AWS.apig(region: region, credentials: credentials).get_base_path_mappings(domain_name: d.domain_name, limit: 500).items
|
|
400
|
+
mappings.each { |m|
|
|
401
|
+
MU.log "Deleting API Gateway Domain Name mapping #{d.domain_name} => #{m.rest_api_id} path #{m.base_path}"
|
|
402
|
+
if !noop
|
|
403
|
+
MU::Cloud::AWS.apig(region: region, credentials: credentials).delete_base_path_mapping(domain_name: d.domain_name, base_path: m.base_path)
|
|
404
|
+
end
|
|
405
|
+
}
|
|
406
|
+
MU.log "Deleting API Gateway Domain Name #{d.domain_name}"
|
|
407
|
+
if !noop
|
|
408
|
+
MU::Cloud::AWS.apig(region: region, credentials: credentials).delete_domain_name(domain_name: d.domain_name)
|
|
409
|
+
end
|
|
410
|
+
end
|
|
411
|
+
}
|
|
412
|
+
end
|
|
413
|
+
|
|
249
414
|
resp = MU::Cloud::AWS.apig(region: region, credentials: credentials).get_rest_apis
|
|
250
415
|
if resp and resp.items
|
|
251
416
|
resp.items.each { |api|
|
|
252
417
|
# The stupid things don't have tags
|
|
253
|
-
if api.description ==
|
|
418
|
+
if api.description == deploy_id
|
|
419
|
+
logs = MU::Cloud.resourceClass("AWS", "Log").find(region: region, credentials: credentials)
|
|
420
|
+
logs.each_pair { |log_id, log_desc|
|
|
421
|
+
if log_id =~ /^API-Gateway-Execution-Logs_#{api.id}\//
|
|
422
|
+
MU.log "Deleting CloudWatch Log Group #{log_id}"
|
|
423
|
+
if !noop
|
|
424
|
+
MU::Cloud::AWS.cloudwatchlogs(region: region, credentials: credentials).delete_log_group(log_group_name: log_id)
|
|
425
|
+
end
|
|
426
|
+
end
|
|
427
|
+
}
|
|
254
428
|
MU.log "Deleting API Gateway #{api.name} (#{api.id})"
|
|
255
429
|
if !noop
|
|
256
430
|
MU::Cloud::AWS.apig(region: region, credentials: credentials).delete_rest_api(
|
|
@@ -260,6 +434,7 @@ MU::Cloud::AWS.apig(region: @config['region'], credentials: @config['credentials
|
|
|
260
434
|
end
|
|
261
435
|
}
|
|
262
436
|
end
|
|
437
|
+
|
|
263
438
|
end
|
|
264
439
|
|
|
265
440
|
# Locate an existing API.
|
|
@@ -283,16 +458,214 @@ MU::Cloud::AWS.apig(region: @config['region'], credentials: @config['credentials
|
|
|
283
458
|
found
|
|
284
459
|
end
|
|
285
460
|
|
|
461
|
+
# Reverse-map our cloud description into a runnable config hash.
|
|
462
|
+
# We assume that any values we have in +@config+ are placeholders, and
|
|
463
|
+
# calculate our own accordingly based on what's live in the cloud.
|
|
464
|
+
def toKitten(**_args)
|
|
465
|
+
bok = {
|
|
466
|
+
"cloud" => "AWS",
|
|
467
|
+
"credentials" => @credentials,
|
|
468
|
+
"cloud_id" => @cloud_id,
|
|
469
|
+
"region" => @region
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
if !cloud_desc
|
|
473
|
+
MU.log "toKitten failed to load a cloud_desc from #{@cloud_id}", MU::ERR, details: @config
|
|
474
|
+
return nil
|
|
475
|
+
end
|
|
476
|
+
|
|
477
|
+
bok['name'] = cloud_desc.name
|
|
478
|
+
|
|
479
|
+
resources = MU::Cloud::AWS.apig(region: @region, credentials: @credentials).get_resources(
|
|
480
|
+
rest_api_id: @cloud_id,
|
|
481
|
+
).items
|
|
482
|
+
|
|
483
|
+
resources.each { |r|
|
|
484
|
+
next if !r.respond_to?(:resource_methods) or r.resource_methods.nil?
|
|
485
|
+
r.resource_methods.each_pair { |http_type, m|
|
|
486
|
+
bok['methods'] ||= []
|
|
487
|
+
method = {}
|
|
488
|
+
m_desc = MU::Cloud::AWS.apig(region: @region, credentials: @credentials).get_method(
|
|
489
|
+
rest_api_id: @cloud_id,
|
|
490
|
+
resource_id: r.id,
|
|
491
|
+
http_method: http_type
|
|
492
|
+
)
|
|
493
|
+
|
|
494
|
+
method['type'] = http_type
|
|
495
|
+
method['path'] = r.path_part || r.path
|
|
496
|
+
if m_desc.method_responses
|
|
497
|
+
m_desc.method_responses.each_pair { |code, resp_desc|
|
|
498
|
+
method['responses'] ||= []
|
|
499
|
+
resp = { "code" => code.to_i }
|
|
500
|
+
if resp_desc.response_parameters
|
|
501
|
+
resp_desc.response_parameters.each_pair { |hdr, reqd|
|
|
502
|
+
resp['headers'] ||= []
|
|
503
|
+
if hdr.match(/^method\.response\.header\.(.*)/)
|
|
504
|
+
resp['headers'] << {
|
|
505
|
+
"header" => Regexp.last_match[1],
|
|
506
|
+
"required" => reqd
|
|
507
|
+
}
|
|
508
|
+
else
|
|
509
|
+
MU.log "I don't know what to do with APIG response parameter #{hdr}", MU::ERR, details: resp_desc
|
|
510
|
+
end
|
|
511
|
+
|
|
512
|
+
}
|
|
513
|
+
end
|
|
514
|
+
if resp_desc.response_models
|
|
515
|
+
resp_desc.response_models.each_pair { |content_type, body|
|
|
516
|
+
resp['body'] ||= []
|
|
517
|
+
resp['body'] << {
|
|
518
|
+
"content_type" => content_type,
|
|
519
|
+
"is_error" => (body == "Error")
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
end
|
|
524
|
+
method['responses'] << resp
|
|
525
|
+
|
|
526
|
+
}
|
|
527
|
+
end
|
|
528
|
+
|
|
529
|
+
if m_desc.method_integration
|
|
530
|
+
if ["AWS", "AWS_PROXY"].include?(m_desc.method_integration.type)
|
|
531
|
+
if m_desc.method_integration.uri.match(/:lambda:path\/\d{4}-\d{2}-\d{2}\/functions\/arn:.*?:function:(.*?)\/invocations$/)
|
|
532
|
+
method['integrate_with'] = MU::Config::Ref.get(
|
|
533
|
+
id: Regexp.last_match[1],
|
|
534
|
+
type: "functions",
|
|
535
|
+
cloud: "AWS",
|
|
536
|
+
integration_http_method: m_desc.method_integration.http_method
|
|
537
|
+
)
|
|
538
|
+
elsif m_desc.method_integration.uri.match(/#{@region}:([^:]+):action\/(.*)/)
|
|
539
|
+
method['integrate_with'] = {
|
|
540
|
+
"type" => "aws_generic",
|
|
541
|
+
"integration_http_method" => m_desc.method_integration.http_method,
|
|
542
|
+
"aws_generic_action" => Regexp.last_match[1]+":"+Regexp.last_match[2]
|
|
543
|
+
}
|
|
544
|
+
else
|
|
545
|
+
MU.log "I don't know what to do with #{m_desc.method_integration.uri}", MU::ERR
|
|
546
|
+
end
|
|
547
|
+
if m_desc.method_integration.http_method
|
|
548
|
+
method['integrate_with']['backend_http_method'] = m_desc.method_integration.http_method
|
|
549
|
+
end
|
|
550
|
+
method['proxy'] = true if m_desc.method_integration.type == "AWS_PROXY"
|
|
551
|
+
elsif m_desc.method_integration.type == "MOCK"
|
|
552
|
+
method['integrate_with'] = {
|
|
553
|
+
"type" => "mock"
|
|
554
|
+
}
|
|
555
|
+
else
|
|
556
|
+
MU.log "I don't know what to do with this integration", MU::ERR, details: m_desc.method_integration
|
|
557
|
+
next
|
|
558
|
+
end
|
|
559
|
+
|
|
560
|
+
if m_desc.method_integration.passthrough_behavior
|
|
561
|
+
method['integrate_with']['passthrough_behavior'] = m_desc.method_integration.passthrough_behavior
|
|
562
|
+
end
|
|
563
|
+
|
|
564
|
+
if m_desc.method_integration.request_templates and
|
|
565
|
+
!m_desc.method_integration.request_templates.empty?
|
|
566
|
+
method['integrate_with']['request_templates'] = m_desc.method_integration.request_templates.keys.map { |rt_content_type, template|
|
|
567
|
+
{ "content_type" => rt_content_type, "template" => template }
|
|
568
|
+
}
|
|
569
|
+
end
|
|
570
|
+
|
|
571
|
+
if m_desc.method_integration.request_parameters
|
|
572
|
+
m_desc.method_integration.request_parameters.each_pair { |k, v|
|
|
573
|
+
if !k.match(/^integration\.request\.(header|querystring|path)\.(.*)/)
|
|
574
|
+
MU.log "Don't know how to handle integration request parameter '#{k}', skipping", MU::WARN
|
|
575
|
+
next
|
|
576
|
+
end
|
|
577
|
+
if Regexp.last_match[1] == "header" and
|
|
578
|
+
Regexp.last_match[2] == "X-Amz-Invocation-Type" and
|
|
579
|
+
v == "'Event'"
|
|
580
|
+
method['integrate_with']['async'] = true
|
|
581
|
+
else
|
|
582
|
+
method['integrate_with']['parameters'] ||= []
|
|
583
|
+
method['integrate_with']['parameters'] << {
|
|
584
|
+
"type" => Regexp.last_match[1],
|
|
585
|
+
"name" => Regexp.last_match[2],
|
|
586
|
+
"value" => v
|
|
587
|
+
}
|
|
588
|
+
end
|
|
589
|
+
}
|
|
590
|
+
end
|
|
591
|
+
end
|
|
592
|
+
|
|
593
|
+
bok['methods'] << method
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
deployment = MU::Cloud::AWS.apig(region: @region, credentials: @credentials).get_deployments(
|
|
598
|
+
rest_api_id: @cloud_id
|
|
599
|
+
).items.sort { |a, b| a.created_date <=> b.created_date }.last
|
|
600
|
+
stages = MU::Cloud::AWS.apig(region: @region, credentials: @credentials).get_stages(
|
|
601
|
+
rest_api_id: @cloud_id,
|
|
602
|
+
deployment_id: deployment.id
|
|
603
|
+
)
|
|
604
|
+
|
|
605
|
+
# XXX we only support a single stage right now, which is a dumb
|
|
606
|
+
# limitation
|
|
607
|
+
stage = stages.item.first
|
|
608
|
+
if stage
|
|
609
|
+
bok['deploy_to'] = stage.stage_name
|
|
610
|
+
if stage.access_log_settings
|
|
611
|
+
bok['log_requests'] = true
|
|
612
|
+
bok['access_logs'] = MU::Config::Ref.get(
|
|
613
|
+
id: stage.access_log_settings.destination_arn.sub(/.*?:([^:]+)$/, '\1'),
|
|
614
|
+
credentials: @credentials,
|
|
615
|
+
region: @region,
|
|
616
|
+
type: "logs",
|
|
617
|
+
cloud: "AWS"
|
|
618
|
+
)
|
|
619
|
+
end
|
|
620
|
+
end
|
|
621
|
+
|
|
622
|
+
|
|
623
|
+
bok
|
|
624
|
+
end
|
|
625
|
+
|
|
286
626
|
# Cloud-specific configuration properties.
|
|
287
627
|
# @param _config [MU::Config]: The calling MU::Config object
|
|
288
628
|
# @return [Array<Array,Hash>]: List of required fields, and json-schema Hash of cloud-specific configuration parameters for this resource
|
|
289
629
|
def self.schema(_config)
|
|
290
630
|
toplevel_required = []
|
|
291
631
|
schema = {
|
|
632
|
+
"domain_names" => {
|
|
633
|
+
"type" => "array",
|
|
634
|
+
"items" => {
|
|
635
|
+
"description" => "Configure optional Custom Domain Names to map to this API endpoint.",
|
|
636
|
+
"type" => "object",
|
|
637
|
+
"properties" => {
|
|
638
|
+
"certificate" => MU::Config::Ref.schema(type: "certificate", desc: "An existing IAM or ACM SSL certificate to bind to this alternate name endpoint.", omit_fields: ["cloud", "tag", "deploy_id"]),
|
|
639
|
+
"dns_record" => MU::Config::DNSZone.records_primitive(need_target: false, default_type: "CNAME", need_zone: true, embedded_type: "endpoint")["items"],
|
|
640
|
+
"unmanaged_name" => {
|
|
641
|
+
"type" => "string",
|
|
642
|
+
"description" => "If +dns_record+ is not specified, we will map this string as a domain name and assume that an external DNS record will be created pointing to us at a later time."
|
|
643
|
+
},
|
|
644
|
+
"endpoint_type" => {
|
|
645
|
+
"type" => "string",
|
|
646
|
+
"description" => "The type of endpoint to create with this domain name.",
|
|
647
|
+
"default" => "REGIONAL",
|
|
648
|
+
"enum" => ["REGIONAL", "EDGE", "PRIVATE"]
|
|
649
|
+
},
|
|
650
|
+
"security_policy" => {
|
|
651
|
+
"type" => "string",
|
|
652
|
+
"default" => "TLS_1_2",
|
|
653
|
+
"enum" => ["TLS_1_0", "TLS_1_2"],
|
|
654
|
+
"description" => "Acceptable TLS cipher suites. +TLS_1_2+ is strongly recommended."
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
},
|
|
292
659
|
"deploy_to" => {
|
|
293
660
|
"type" => "string",
|
|
294
661
|
"description" => "The name of an environment under which to deploy our API. If not specified, will deploy to the name of the global Mu environment for this deployment."
|
|
295
662
|
},
|
|
663
|
+
"log_requests" => {
|
|
664
|
+
"type" => "boolean",
|
|
665
|
+
"description" => "Log custom access requests to CloudWatch Logs to the log group specified by +access_logs+, as well as enabling built-in CloudWatch Logs at +INFO+ level. If +access_logs+ is unspecified, a reasonable group will be created automatically.",
|
|
666
|
+
"default" => true
|
|
667
|
+
},
|
|
668
|
+
"access_logs" => MU::Config::Ref.schema(type: "logs", desc: "A pre-existing or sibling Mu Cloudwatch Log group reference. If +log_requests+ is specified and this is not, a log group will be generated automatically. Setting this parameter explicitly automatically enables +log_requests+."),
|
|
296
669
|
"methods" => {
|
|
297
670
|
"items" => {
|
|
298
671
|
"type" => "object",
|
|
@@ -303,16 +676,48 @@ MU::Cloud::AWS.apig(region: @config['region'], credentials: @config['credentials
|
|
|
303
676
|
"type" => "object",
|
|
304
677
|
"description" => "Specify what application backend to invoke under this path/method combination",
|
|
305
678
|
"properties" => {
|
|
679
|
+
"async" => {
|
|
680
|
+
"type" => "boolean",
|
|
681
|
+
"default" => false,
|
|
682
|
+
"description" => "For non-proxy Lambda integrations, adds a static +X-Amz-Invocation-Type+ with value +'Event'+ to invoke the function asynchronously. See also https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-integration-async.html"
|
|
683
|
+
},
|
|
684
|
+
"parameters" => {
|
|
685
|
+
"type" => "array",
|
|
686
|
+
"items" => {
|
|
687
|
+
"description" => "One or headers, paths, or query string parameters to pass as request parameters to our back end. See also: https://docs.aws.amazon.com/apigateway/latest/developerguide/request-response-data-mappings.html",
|
|
688
|
+
"type" => "object",
|
|
689
|
+
"properties" => {
|
|
690
|
+
"name" => {
|
|
691
|
+
"type" => "string",
|
|
692
|
+
"description" => "A valid and unique integration request parameter name."
|
|
693
|
+
},
|
|
694
|
+
"value" => {
|
|
695
|
+
"type" => "string",
|
|
696
|
+
"description" => "The name of a method request parameter, or a static value contained in single quotes (+'foo'+)."
|
|
697
|
+
},
|
|
698
|
+
"type" => {
|
|
699
|
+
"type" => "string",
|
|
700
|
+
"description" => "Which HTTP artifact to use when presenting the parameter to the back end. ",
|
|
701
|
+
"enum" => ["header", "querystring", "path"]
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
},
|
|
306
706
|
"proxy" => {
|
|
307
707
|
"type" => "boolean",
|
|
308
708
|
"default" => false,
|
|
309
|
-
"description" => "
|
|
709
|
+
"description" => "Sets HTTP integrations to HTTP_PROXY and AWS/LAMBDA integrations to AWS_PROXY/LAMBDA_PROXY"
|
|
310
710
|
},
|
|
311
711
|
"backend_http_method" => {
|
|
312
712
|
"type" => "string",
|
|
313
713
|
"description" => "The HTTP method to use when contacting our integrated backend. If not specified, this will be set to match our front end.",
|
|
314
714
|
"enum" => ["GET", "POST", "PUT", "HEAD", "DELETE", "CONNECT", "OPTIONS", "TRACE"],
|
|
315
715
|
},
|
|
716
|
+
"timeout_in_millis" => {
|
|
717
|
+
"type" => "integer",
|
|
718
|
+
"description" => "Custom timeout between +50+ and +29,000+ milliseconds.",
|
|
719
|
+
"default" => 29000
|
|
720
|
+
},
|
|
316
721
|
"url" => {
|
|
317
722
|
"type" => "string",
|
|
318
723
|
"description" => "For HTTP or HTTP_PROXY integrations, this should be a fully-qualified URL"
|
|
@@ -380,14 +785,13 @@ MU::Cloud::AWS.apig(region: @config['region'], credentials: @config['credentials
|
|
|
380
785
|
"description" => "A Mu resource name, for integrations with a sibling resource (e.g. a Function)"
|
|
381
786
|
},
|
|
382
787
|
"cors" => {
|
|
383
|
-
"type" => "
|
|
384
|
-
"description" => "When enabled, this will create an +OPTIONS+ method under this path with request and response header mappings that implement Cross-Origin Resource Sharing",
|
|
385
|
-
"default" => true
|
|
788
|
+
"type" => "string",
|
|
789
|
+
"description" => "When enabled, this will create an +OPTIONS+ method under this path with request and response header mappings that implement Cross-Origin Resource Sharing, setting +Access-Control-Allow-Origin+ to the specified value.",
|
|
386
790
|
},
|
|
387
791
|
"type" => {
|
|
388
792
|
"type" => "string",
|
|
389
793
|
"description" => "A Mu resource type, for integrations with a sibling resource (e.g. a function), or the string +aws_generic+, which we can use in combination with +aws_generic_action+ to integrate with arbitrary AWS services.",
|
|
390
|
-
"enum" => ["aws_generic"].concat(MU::Cloud.resource_types.values.map { |t| t[:
|
|
794
|
+
"enum" => ["aws_generic"].concat(MU::Cloud.resource_types.values.map { |t| t[:cfg_plural] }.sort)
|
|
391
795
|
},
|
|
392
796
|
"aws_generic_action" => {
|
|
393
797
|
"type" => "string",
|
|
@@ -456,7 +860,7 @@ MU::Cloud::AWS.apig(region: @config['region'], credentials: @config['credentials
|
|
|
456
860
|
# Canonical Amazon Resource Number for this resource
|
|
457
861
|
# @return [String]
|
|
458
862
|
def arn
|
|
459
|
-
"arn:#{MU::Cloud::AWS.isGovCloud?(@
|
|
863
|
+
"arn:#{MU::Cloud::AWS.isGovCloud?(@region) ? "aws-us-gov" : "aws"}:execute-api:#{@region}:#{MU::Cloud::AWS.credToAcct(@credentials)}:#{@cloud_id}"
|
|
460
864
|
end
|
|
461
865
|
|
|
462
866
|
|
|
@@ -467,9 +871,89 @@ MU::Cloud::AWS.apig(region: @config['region'], credentials: @config['credentials
|
|
|
467
871
|
def self.validateConfig(endpoint, configurator)
|
|
468
872
|
ok = true
|
|
469
873
|
|
|
874
|
+
if endpoint['log_requests'] and !endpoint['access_logs']
|
|
875
|
+
logdesc = {
|
|
876
|
+
"name" => endpoint['name']+"accesslogs",
|
|
877
|
+
}
|
|
878
|
+
logdesc["tags"] = endpoint["tags"] if endpoint['tags']
|
|
879
|
+
configurator.insertKitten(logdesc, "logs")
|
|
880
|
+
endpoint['access_logs'] = MU::Config::Ref.get(
|
|
881
|
+
name: endpoint['name']+"accesslogs",
|
|
882
|
+
type: "log",
|
|
883
|
+
cloud: "AWS",
|
|
884
|
+
credentials: endpoint['credentials'],
|
|
885
|
+
region: endpoint['region']
|
|
886
|
+
)
|
|
887
|
+
end
|
|
888
|
+
|
|
889
|
+
if endpoint['access_logs'] and endpoint["access_logs"]["name"]
|
|
890
|
+
endpoint['log_requests'] = true
|
|
891
|
+
MU::Config.addDependency(endpoint, endpoint["access_logs"]["name"], "log")
|
|
892
|
+
end
|
|
893
|
+
|
|
894
|
+
if endpoint['access_logs']
|
|
895
|
+
resp = MU::Cloud::AWS.apig(credentials: endpoint['credentials'], region: endpoint['region']).get_account
|
|
896
|
+
if !resp.cloudwatch_role_arn
|
|
897
|
+
MU.log "Endpoint '#{endpoint['name']}' is configured to use CloudWatch Logs, but the account-wide API Gateway log role is not configured", MU::ERR, details: "https://aws.amazon.com/premiumsupport/knowledge-center/api-gateway-cloudwatch-logs/"
|
|
898
|
+
ok = false
|
|
899
|
+
else
|
|
900
|
+
roles = MU::Cloud::AWS::Role.find(cloud_id: resp.cloudwatch_role_arn, credentials: endpoint['credentials'], region: endpoint['region'])
|
|
901
|
+
if roles.empty?
|
|
902
|
+
MU.log "Endpoint '#{endpoint['name']}' is configured to use CloudWatch Logs, but the configured account-wide API Gateway log role does not exist", MU::ERR, details: resp.cloudwatch_role_arn
|
|
903
|
+
ok = false
|
|
904
|
+
end
|
|
905
|
+
end
|
|
906
|
+
end
|
|
907
|
+
|
|
908
|
+
if endpoint['domain_names']
|
|
909
|
+
endpoint['domain_names'].each { |dom|
|
|
910
|
+
if dom['certificate']
|
|
911
|
+
cert_arn, cert_domains = MU::Cloud::AWS.resolveSSLCertificate(dom['certificate'], region: dom['region'], credentials: dom['credentials'])
|
|
912
|
+
if !cert_arn
|
|
913
|
+
MU.log "API Gateway #{endpoint['name']}: Failed to resolve SSL certificate in domain_name block", MU::ERR, details: dom
|
|
914
|
+
ok = false
|
|
915
|
+
end
|
|
916
|
+
end
|
|
917
|
+
if !dom['unmanaged_name'] and !dom['dns_record']
|
|
918
|
+
MU.log "API Gateway #{endpoint['name']}: Must specify either unmanaged_name or dns_record in domain_name block", MU::ERR, details: dom
|
|
919
|
+
ok = false
|
|
920
|
+
end
|
|
921
|
+
|
|
922
|
+
# Make at least an attempt to catch when we've specified the same
|
|
923
|
+
# DNS name to point to both the main gateway and this alternative
|
|
924
|
+
# endpoint, because that ish won't work. This check will miss if
|
|
925
|
+
# the end user specifies the zone in competing ways.
|
|
926
|
+
if dom['dns_record'] and endpoint['dns_records']
|
|
927
|
+
endpoint['dns_records'].each { |rec|
|
|
928
|
+
if rec['name'] == dom['dns_record']['name'] and
|
|
929
|
+
rec['zone'] == dom['dns_record']['zone']
|
|
930
|
+
MU.log "API Gateway #{endpoint['name']}: Cannot specify same entry in dns_records and domain_names", MU::ERR, details: rec
|
|
931
|
+
ok = false
|
|
932
|
+
end
|
|
933
|
+
}
|
|
934
|
+
end
|
|
935
|
+
}
|
|
936
|
+
end
|
|
937
|
+
|
|
470
938
|
append = []
|
|
471
939
|
endpoint['deploy_to'] ||= MU.environment || $environment || "dev"
|
|
472
940
|
endpoint['methods'].each { |m|
|
|
941
|
+
if m['integrate_with']['async']
|
|
942
|
+
if m['integrate_with']['type'] == "functions" and
|
|
943
|
+
m['integrate_with']['async']
|
|
944
|
+
m['integrate_with']['parameters'] ||= []
|
|
945
|
+
m['integrate_with']['parameters'] << {
|
|
946
|
+
"name" => "X-Amz-Invocation-Type",
|
|
947
|
+
"value" => "'Event'", # yes the single quotes are required
|
|
948
|
+
"type" => "header"
|
|
949
|
+
}
|
|
950
|
+
if m['integrate_with']['proxy']
|
|
951
|
+
MU.log "Cannot specify both of proxy and async for API Gateway method integration", MU::ERR
|
|
952
|
+
ok = false
|
|
953
|
+
end
|
|
954
|
+
end
|
|
955
|
+
end
|
|
956
|
+
|
|
473
957
|
if m['integrate_with'] and m['integrate_with']['name']
|
|
474
958
|
if m['integrate_with']['type'] != "aws_generic"
|
|
475
959
|
MU::Config.addDependency(endpoint, m['integrate_with']['name'], m['integrate_with']['type'])
|
|
@@ -486,15 +970,16 @@ MU::Cloud::AWS.apig(region: @config['region'], credentials: @config['credentials
|
|
|
486
970
|
r['headers'] ||= []
|
|
487
971
|
r['headers'] << {
|
|
488
972
|
"header" => "Access-Control-Allow-Origin",
|
|
489
|
-
"value" =>
|
|
973
|
+
"value" => m['cors'],
|
|
490
974
|
"required" => true
|
|
491
975
|
}
|
|
492
976
|
r['headers'].uniq!
|
|
493
977
|
}
|
|
494
978
|
|
|
495
|
-
append << cors_option_integrations(m['path'])
|
|
979
|
+
append << cors_option_integrations(m['path'], m['cors'])
|
|
496
980
|
end
|
|
497
981
|
|
|
982
|
+
|
|
498
983
|
if !m['iam_role']
|
|
499
984
|
m['uri'] ||= "*" if m['integrate_with']['type'] == "aws_generic"
|
|
500
985
|
|
|
@@ -516,7 +1001,7 @@ MU::Cloud::AWS.apig(region: @config['region'], credentials: @config['credentials
|
|
|
516
1001
|
"targets" => [{ "identifier" => m['uri'] }]
|
|
517
1002
|
}
|
|
518
1003
|
]
|
|
519
|
-
elsif m['integrate_with']['type'] == "
|
|
1004
|
+
elsif m['integrate_with']['type'] == "functions"
|
|
520
1005
|
roledesc["import"] = ["AWSLambdaBasicExecutionRole"]
|
|
521
1006
|
end
|
|
522
1007
|
configurator.insertKitten(roledesc, "roles")
|
|
@@ -534,7 +1019,7 @@ MU::Cloud::AWS.apig(region: @config['region'], credentials: @config['credentials
|
|
|
534
1019
|
ok
|
|
535
1020
|
end
|
|
536
1021
|
|
|
537
|
-
def self.cors_option_integrations(path)
|
|
1022
|
+
def self.cors_option_integrations(path, origins)
|
|
538
1023
|
{
|
|
539
1024
|
"type" => "OPTIONS",
|
|
540
1025
|
"path" => path,
|
|
@@ -555,7 +1040,7 @@ MU::Cloud::AWS.apig(region: @config['region'], credentials: @config['credentials
|
|
|
555
1040
|
},
|
|
556
1041
|
{
|
|
557
1042
|
"header" => "Access-Control-Allow-Origin",
|
|
558
|
-
"value" =>
|
|
1043
|
+
"value" => origins,
|
|
559
1044
|
"required" => true
|
|
560
1045
|
}
|
|
561
1046
|
],
|