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
|
@@ -33,7 +33,7 @@ module MU
|
|
|
33
33
|
namestr += ".fifo" if attrs['FifoQueue']
|
|
34
34
|
|
|
35
35
|
MU.log "Creating SQS queue #{namestr}", details: attrs
|
|
36
|
-
resp = MU::Cloud::AWS.sqs(region: @
|
|
36
|
+
resp = MU::Cloud::AWS.sqs(region: @region, credentials: @credentials).create_queue(
|
|
37
37
|
queue_name: namestr,
|
|
38
38
|
attributes: attrs
|
|
39
39
|
)
|
|
@@ -60,7 +60,7 @@ module MU
|
|
|
60
60
|
}
|
|
61
61
|
if changed
|
|
62
62
|
MU.log "Updating SQS queue #{@mu_name}", MU::NOTICE, details: new_attrs
|
|
63
|
-
MU::Cloud::AWS.sqs(region: @
|
|
63
|
+
MU::Cloud::AWS.sqs(region: @region, credentials: @credentials).set_queue_attributes(
|
|
64
64
|
queue_url: @cloud_id,
|
|
65
65
|
attributes: new_attrs
|
|
66
66
|
)
|
|
@@ -71,7 +71,7 @@ module MU
|
|
|
71
71
|
# Canonical Amazon Resource Number for this resource
|
|
72
72
|
# @return [String]
|
|
73
73
|
def arn
|
|
74
|
-
"arn:"+(MU::Cloud::AWS.isGovCloud?(@
|
|
74
|
+
"arn:"+(MU::Cloud::AWS.isGovCloud?(@region) ? "aws-us-gov" : "aws")+":sqs:"+@region+":"+MU::Cloud::AWS.credToAcct(@credentials)+":"+@cloud_id
|
|
75
75
|
end
|
|
76
76
|
|
|
77
77
|
@cloud_desc_cache = nil
|
|
@@ -80,9 +80,10 @@ module MU
|
|
|
80
80
|
# @return [Hash]: AWS doesn't return anything but the SQS URL, so supplement with attributes
|
|
81
81
|
def cloud_desc(use_cache: true)
|
|
82
82
|
return @cloud_desc_cache if @cloud_desc_cache and use_cache
|
|
83
|
+
return nil if !@cloud_id
|
|
83
84
|
|
|
84
85
|
if !@cloud_id
|
|
85
|
-
resp = MU::Cloud::AWS.sqs(region: @
|
|
86
|
+
resp = MU::Cloud::AWS.sqs(region: @region, credentials: @credentials).list_queues(
|
|
86
87
|
queue_name_prefix: @mu_name
|
|
87
88
|
)
|
|
88
89
|
return nil if !resp or !resp.queue_urls
|
|
@@ -97,8 +98,8 @@ module MU
|
|
|
97
98
|
return nil if !@cloud_id
|
|
98
99
|
@cloud_desc_cache = MU::Cloud::AWS::MsgQueue.find(
|
|
99
100
|
cloud_id: @cloud_id.dup,
|
|
100
|
-
region: @
|
|
101
|
-
credentials: @
|
|
101
|
+
region: @region,
|
|
102
|
+
credentials: @credentials
|
|
102
103
|
)
|
|
103
104
|
@cloud_desc_cache
|
|
104
105
|
end
|
|
@@ -109,8 +110,8 @@ module MU
|
|
|
109
110
|
cloud_desc
|
|
110
111
|
deploy_struct = MU::Cloud::AWS::MsgQueue.find(
|
|
111
112
|
cloud_id: @cloud_id,
|
|
112
|
-
region: @
|
|
113
|
-
credentials: @
|
|
113
|
+
region: @region,
|
|
114
|
+
credentials: @credentials
|
|
114
115
|
)
|
|
115
116
|
return deploy_struct
|
|
116
117
|
end
|
|
@@ -133,12 +134,12 @@ module MU
|
|
|
133
134
|
# @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
|
|
134
135
|
# @param region [String]: The cloud provider region
|
|
135
136
|
# @return [void]
|
|
136
|
-
def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
|
|
137
|
+
def self.cleanup(noop: false, deploy_id: MU.deploy_id, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
|
|
137
138
|
MU.log "AWS::MsgQueue.cleanup: need to support flags['known']", MU::DEBUG, details: flags
|
|
138
139
|
MU.log "Placeholder: AWS MsgQueue artifacts do not support tags, so ignoremaster cleanup flag has no effect", MU::DEBUG, details: ignoremaster
|
|
139
140
|
|
|
140
141
|
resp = MU::Cloud::AWS.sqs(credentials: credentials, region: region).list_queues(
|
|
141
|
-
queue_name_prefix:
|
|
142
|
+
queue_name_prefix: deploy_id
|
|
142
143
|
)
|
|
143
144
|
if resp and resp.queue_urls
|
|
144
145
|
threads = []
|
|
@@ -194,7 +195,15 @@ module MU
|
|
|
194
195
|
|
|
195
196
|
# Go fetch its attributes
|
|
196
197
|
fetch = if args[:cloud_id]
|
|
197
|
-
|
|
198
|
+
if args[:cloud_id] !~ /^https?:\/\//
|
|
199
|
+
[begin
|
|
200
|
+
MU::Cloud::AWS.sqs(region: args[:region], credentials: args[:credentials]).get_queue_url(queue_name: args[:cloud_id]).queue_url
|
|
201
|
+
rescue Aws::SQS::Errors::NonExistentQueue
|
|
202
|
+
return found
|
|
203
|
+
end]
|
|
204
|
+
else
|
|
205
|
+
[args[:cloud_id]]
|
|
206
|
+
end
|
|
198
207
|
else
|
|
199
208
|
resp = MU::Cloud::AWS.sqs(region: args[:region], credentials: args[:credentials]).list_queues
|
|
200
209
|
resp.queue_urls
|
|
@@ -417,7 +426,7 @@ module MU
|
|
|
417
426
|
if sibling # resolve sibling queues to something useful
|
|
418
427
|
id = sibling.cloud_id
|
|
419
428
|
end
|
|
420
|
-
desc = MU::Cloud::AWS::MsgQueue.find(cloud_id: id, credentials: @
|
|
429
|
+
desc = MU::Cloud::AWS::MsgQueue.find(cloud_id: id, credentials: @credentials)
|
|
421
430
|
if !desc
|
|
422
431
|
raise MuError, "Failed to get cloud descriptor for SQS queue #{@config['failqueue']['name']}"
|
|
423
432
|
end
|
|
@@ -475,7 +484,7 @@ module MU
|
|
|
475
484
|
end
|
|
476
485
|
|
|
477
486
|
begin
|
|
478
|
-
MU::Cloud::AWS.sqs(region: @
|
|
487
|
+
MU::Cloud::AWS.sqs(region: @region, credentials: @credentials).tag_queue(
|
|
479
488
|
queue_url: url,
|
|
480
489
|
tags: tags
|
|
481
490
|
)
|
|
@@ -47,7 +47,11 @@ module MU
|
|
|
47
47
|
}
|
|
48
48
|
end
|
|
49
49
|
|
|
50
|
+
type_map = {}
|
|
51
|
+
|
|
50
52
|
@config['attributes'].each { |attr|
|
|
53
|
+
type_map[attr['name']] = attr['type']
|
|
54
|
+
|
|
51
55
|
params[:attribute_definitions] << {
|
|
52
56
|
:attribute_name => attr['name'],
|
|
53
57
|
:attribute_type => attr['type']
|
|
@@ -67,6 +71,11 @@ module MU
|
|
|
67
71
|
}
|
|
68
72
|
end
|
|
69
73
|
}
|
|
74
|
+
# apparently the HASH key always has to be before RANGE, so sort it
|
|
75
|
+
# lexically by that field and call it a day
|
|
76
|
+
params[:key_schema].sort! { |a, b|
|
|
77
|
+
a[:key_type] <=> b[:key_type]
|
|
78
|
+
}
|
|
70
79
|
|
|
71
80
|
if @config['secondary_indexes']
|
|
72
81
|
@config['secondary_indexes'].each { |idx|
|
|
@@ -99,18 +108,38 @@ module MU
|
|
|
99
108
|
}
|
|
100
109
|
end
|
|
101
110
|
|
|
102
|
-
|
|
111
|
+
if @tags
|
|
112
|
+
params[:tags] = @tags.each_key.map { |k| { :key => k, :value => @tags[k] } }
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
MU.log "Creating DynamoDB table #{@mu_name}", MU::NOTICE, details: params
|
|
103
116
|
|
|
104
|
-
resp = MU::Cloud::AWS.dynamo(credentials: @
|
|
117
|
+
resp = MU::Cloud::AWS.dynamo(credentials: @credentials, region: @region).create_table(params)
|
|
105
118
|
@cloud_id = @mu_name
|
|
106
119
|
|
|
107
120
|
begin
|
|
108
|
-
resp = MU::Cloud::AWS.dynamo(credentials: @
|
|
121
|
+
resp = MU::Cloud::AWS.dynamo(credentials: @credentials, region: @region).describe_table(table_name: @cloud_id)
|
|
109
122
|
sleep 5 if resp.table.table_status == "CREATING"
|
|
110
123
|
end while resp.table.table_status == "CREATING"
|
|
111
124
|
|
|
112
|
-
|
|
113
125
|
tagTable if !@config['scrub_mu_isms']
|
|
126
|
+
|
|
127
|
+
if @config['populate'] and !@config['populate'].empty?
|
|
128
|
+
MU.log "Preloading #{@mu_name} with #{@config['populate'].size.to_s} items"
|
|
129
|
+
items_to_write = @config['populate'].dup
|
|
130
|
+
begin
|
|
131
|
+
batch = items_to_write.slice!(0, (items_to_write.length >= 25 ? 25 : items_to_write.length))
|
|
132
|
+
begin
|
|
133
|
+
MU::Cloud::AWS.dynamo(credentials: @credentials, region: @region).batch_write_item(
|
|
134
|
+
request_items: {
|
|
135
|
+
@cloud_id => batch.map { |i| { put_request: { item: i } } }
|
|
136
|
+
}
|
|
137
|
+
)
|
|
138
|
+
rescue ::Aws::DynamoDB::Errors::ValidationException => e
|
|
139
|
+
MU.log e.message, MU::ERR, details: item
|
|
140
|
+
end
|
|
141
|
+
end while !items_to_write.empty?
|
|
142
|
+
end
|
|
114
143
|
end
|
|
115
144
|
|
|
116
145
|
# Apply tags to this DynamoDB table
|
|
@@ -133,7 +162,7 @@ module MU
|
|
|
133
162
|
}
|
|
134
163
|
end
|
|
135
164
|
|
|
136
|
-
MU::Cloud::AWS.dynamo(credentials: @
|
|
165
|
+
MU::Cloud::AWS.dynamo(credentials: @credentials, region: @region).tag_resource(
|
|
137
166
|
resource_arn: arn,
|
|
138
167
|
tags: tagset
|
|
139
168
|
)
|
|
@@ -143,6 +172,7 @@ module MU
|
|
|
143
172
|
# Called automatically by {MU::Deploy#createResources}
|
|
144
173
|
def groom
|
|
145
174
|
tagTable if !@config['scrub_mu_isms']
|
|
175
|
+
MU.log "NoSQL Table #{@config['name']}: #{@cloud_id}", MU::SUMMARY
|
|
146
176
|
end
|
|
147
177
|
|
|
148
178
|
# Does this resource type exist as a global (cloud-wide) artifact, or
|
|
@@ -163,7 +193,7 @@ module MU
|
|
|
163
193
|
# @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
|
|
164
194
|
# @param region [String]: The cloud provider region
|
|
165
195
|
# @return [void]
|
|
166
|
-
def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
|
|
196
|
+
def self.cleanup(noop: false, deploy_id: MU.deploy_id, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
|
|
167
197
|
MU.log "AWS::NoSQLDb.cleanup: need to support flags['known']", MU::DEBUG, details: flags
|
|
168
198
|
|
|
169
199
|
resp = MU::Cloud::AWS.dynamo(credentials: credentials, region: region).list_tables
|
|
@@ -183,7 +213,7 @@ module MU
|
|
|
183
213
|
deploy_match = false
|
|
184
214
|
master_match = false
|
|
185
215
|
tags.tags.each { |tag|
|
|
186
|
-
if tag.key == "MU-ID" and tag.value ==
|
|
216
|
+
if tag.key == "MU-ID" and tag.value == deploy_id
|
|
187
217
|
deploy_match = true
|
|
188
218
|
elsif tag.key == "MU-MASTER-IP" and tag.value == MU.mu_public_ip
|
|
189
219
|
master_match = true
|
|
@@ -214,7 +244,8 @@ module MU
|
|
|
214
244
|
# Return the metadata for this user cofiguration
|
|
215
245
|
# @return [Hash]
|
|
216
246
|
def notify
|
|
217
|
-
|
|
247
|
+
return nil if !@cloud_id or !cloud_desc(use_cache: false)
|
|
248
|
+
MU.structToHash(cloud_desc, stringify_keys: true)
|
|
218
249
|
end
|
|
219
250
|
|
|
220
251
|
# Locate an existing DynamoDB table
|
|
@@ -244,6 +275,59 @@ module MU
|
|
|
244
275
|
found
|
|
245
276
|
end
|
|
246
277
|
|
|
278
|
+
# Reverse-map our cloud description into a runnable config hash.
|
|
279
|
+
# We assume that any values we have in +@config+ are placeholders, and
|
|
280
|
+
# calculate our own accordingly based on what's live in the cloud.
|
|
281
|
+
def toKitten(**_args)
|
|
282
|
+
bok = {
|
|
283
|
+
"cloud" => "AWS",
|
|
284
|
+
"credentials" => @credentials,
|
|
285
|
+
"cloud_id" => @cloud_id,
|
|
286
|
+
"region" => @region
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if !cloud_desc
|
|
290
|
+
MU.log "toKitten failed to load a cloud_desc from #{@cloud_id}", MU::ERR, details: @config
|
|
291
|
+
return nil
|
|
292
|
+
end
|
|
293
|
+
bok['name'] = cloud_desc.table_name
|
|
294
|
+
bok['read_capacity'] = cloud_desc.provisioned_throughput.read_capacity_units
|
|
295
|
+
bok['write_capacity'] = cloud_desc.provisioned_throughput.write_capacity_units
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
cloud_desc.attribute_definitions.each { |attr|
|
|
299
|
+
bok['attributes'] ||= []
|
|
300
|
+
newattr = {
|
|
301
|
+
"name" => attr.attribute_name,
|
|
302
|
+
"type" => attr.attribute_type
|
|
303
|
+
}
|
|
304
|
+
if cloud_desc.key_schema
|
|
305
|
+
cloud_desc.key_schema.each { |key|
|
|
306
|
+
next if key.attribute_name == attr.attribute_name
|
|
307
|
+
if key.key_type == "RANGE"
|
|
308
|
+
newattr["primary_partition"] = true
|
|
309
|
+
elsif key.key_type == "HASH"
|
|
310
|
+
newattr["primary_sort"] = true
|
|
311
|
+
end
|
|
312
|
+
}
|
|
313
|
+
end
|
|
314
|
+
bok['attributes'] << newattr
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
if cloud_desc.stream_specification and cloud_desc.stream_specification.stream_enabled
|
|
318
|
+
|
|
319
|
+
bok['stream'] = cloud_desc.stream_specification.stream_view_type
|
|
320
|
+
# cloud_desc.latest_stream_arn
|
|
321
|
+
# MU::Cloud::AWS.dynamostream(credentials: @credentials, region: @region).list_streams
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
bok["populate"] = MU::Cloud::AWS.dynamo(credentials: @credentials, region: @region).scan(
|
|
325
|
+
table_name: @cloud_id
|
|
326
|
+
).items
|
|
327
|
+
|
|
328
|
+
bok
|
|
329
|
+
end
|
|
330
|
+
|
|
247
331
|
# Cloud-specific configuration properties.
|
|
248
332
|
# @param _config [MU::Config]: The calling MU::Config object
|
|
249
333
|
# @return [Array<Array,Hash>]: List of required fields, and json-schema Hash of cloud-specific configuration parameters for this resource
|
|
@@ -252,6 +336,13 @@ module MU
|
|
|
252
336
|
|
|
253
337
|
|
|
254
338
|
schema = {
|
|
339
|
+
"populate" => {
|
|
340
|
+
"type" => "array",
|
|
341
|
+
"items" => {
|
|
342
|
+
"type" => "object",
|
|
343
|
+
"description" => "Key-value pairs, compatible with the +attributes+ schema, with which to populate this +table+ during its initial creation."
|
|
344
|
+
}
|
|
345
|
+
},
|
|
255
346
|
"attributes" => {
|
|
256
347
|
"type" => "array",
|
|
257
348
|
"minItems" => 1,
|
|
@@ -27,8 +27,8 @@ module MU
|
|
|
27
27
|
|
|
28
28
|
# Called automatically by {MU::Deploy#createResources}
|
|
29
29
|
def create
|
|
30
|
-
MU::Cloud::AWS.sns(region: @config['region'], credentials: @config['credentials']).create_topic(name: @mu_name)
|
|
31
30
|
@cloud_id = @mu_name
|
|
31
|
+
MU::Cloud::AWS.sns(region: @region, credentials: @credentials).create_topic(name: @cloud_id)
|
|
32
32
|
MU.log "Created SNS topic #{@mu_name}"
|
|
33
33
|
end
|
|
34
34
|
|
|
@@ -36,17 +36,48 @@ module MU
|
|
|
36
36
|
def groom
|
|
37
37
|
if @config['subscriptions']
|
|
38
38
|
@config['subscriptions'].each { |sub|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
39
|
+
if sub['resource'] and !sub['endpoint']
|
|
40
|
+
endpoint_obj = nil
|
|
41
|
+
MU.retrier([], max: 5, wait: 9, loop_if: Proc.new { endpoint_obj.nil? }) {
|
|
42
|
+
endpoint_obj = MU::Config::Ref.get(sub['resource']).kitten(@deploy)
|
|
43
|
+
}
|
|
44
|
+
sub['endpoint'] = endpoint_obj.arn
|
|
45
|
+
end
|
|
46
|
+
subscribe(sub['endpoint'], sub['type'])
|
|
46
47
|
}
|
|
47
48
|
end
|
|
48
49
|
end
|
|
49
50
|
|
|
51
|
+
# Subscribe something to this SNS topic
|
|
52
|
+
# @param endpoint [String]: The address, identifier, or ARN of the resource being subscribed
|
|
53
|
+
# @param protocol [String]: The protocol being subscribed
|
|
54
|
+
def subscribe(endpoint, protocol)
|
|
55
|
+
self.class.subscribe(arn, endpoint, protocol, region: @region, credentials: @credentials)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Subscribe something to an SNS topic
|
|
59
|
+
# @param cloud_id [String]: The short name or ARN of an existing SNS topic
|
|
60
|
+
# @param endpoint [String]: The address, identifier, or ARN of the resource being subscribed
|
|
61
|
+
# @param protocol [String]: The protocol being subscribed
|
|
62
|
+
# @param region [String]: The region of the target SNS topic
|
|
63
|
+
# @param credentials [String]:
|
|
64
|
+
def self.subscribe(cloud_id, endpoint, protocol, region: nil, credentials: nil)
|
|
65
|
+
topic = find(cloud_id: cloud_id, region: region, credentials: credentials).values.first
|
|
66
|
+
if !topic
|
|
67
|
+
raise MuError, "Failed to find SNS Topic #{cloud_id} in #{region}"
|
|
68
|
+
end
|
|
69
|
+
arn = topic["TopicArn"]
|
|
70
|
+
|
|
71
|
+
resp = MU::Cloud::AWS.sns(region: region, credentials: credentials).list_subscriptions_by_topic(topic_arn: arn).subscriptions
|
|
72
|
+
|
|
73
|
+
resp.each { |subscription|
|
|
74
|
+
return subscription if subscription.protocol == protocol and subscription.endpoint == endpoint
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
MU.log "Subscribing #{endpoint} (#{protocol}) to SNS topic #{arn}", MU::NOTICE
|
|
78
|
+
MU::Cloud::AWS.sns(region: region, credentials: credentials).subscribe(topic_arn: arn, protocol: protocol, endpoint: endpoint)
|
|
79
|
+
end
|
|
80
|
+
|
|
50
81
|
# Does this resource type exist as a global (cloud-wide) artifact, or
|
|
51
82
|
# is it localized to a region/zone?
|
|
52
83
|
# @return [Boolean]
|
|
@@ -65,12 +96,12 @@ module MU
|
|
|
65
96
|
# @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
|
|
66
97
|
# @param region [String]: The cloud provider region
|
|
67
98
|
# @return [void]
|
|
68
|
-
def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
|
|
99
|
+
def self.cleanup(noop: false, deploy_id: MU.deploy_id, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
|
|
69
100
|
MU.log "AWS::Notifier.cleanup: need to support flags['known']", MU::DEBUG, details: flags
|
|
70
101
|
MU.log "Placeholder: AWS Notifier artifacts do not support tags, so ignoremaster cleanup flag has no effect", MU::DEBUG, details: ignoremaster
|
|
71
102
|
|
|
72
103
|
MU::Cloud::AWS.sns(region: region, credentials: credentials).list_topics.topics.each { |topic|
|
|
73
|
-
if topic.topic_arn.match(
|
|
104
|
+
if topic.topic_arn.match(deploy_id)
|
|
74
105
|
# We don't have a way to tag our SNS topics, so we will delete any topic that has the MU-ID in its ARN.
|
|
75
106
|
# This may fail to find notifier groups in some cases (eg. cache_cluster) so we might want to delete from each API as well.
|
|
76
107
|
MU.log "Deleting SNS topic: #{topic.topic_arn}"
|
|
@@ -85,13 +116,14 @@ module MU
|
|
|
85
116
|
# @return [String]
|
|
86
117
|
def arn
|
|
87
118
|
@cloud_id ||= @mu_name
|
|
88
|
-
"arn:"+(MU::Cloud::AWS.isGovCloud?(@
|
|
119
|
+
"arn:"+(MU::Cloud::AWS.isGovCloud?(@region) ? "aws-us-gov" : "aws")+":sns:"+@region+":"+MU::Cloud::AWS.credToAcct(@credentials)+":"+@cloud_id
|
|
89
120
|
end
|
|
90
121
|
|
|
91
122
|
# Return the metadata for this user cofiguration
|
|
92
123
|
# @return [Hash]
|
|
93
124
|
def notify
|
|
94
|
-
|
|
125
|
+
return nil if !@cloud_id or !cloud_desc(use_cache: false)
|
|
126
|
+
desc = MU::Cloud::AWS.sns(region: @region, credentials: @credentials).get_topic_attributes(topic_arn: arn).attributes
|
|
95
127
|
MU.structToHash(desc)
|
|
96
128
|
end
|
|
97
129
|
|
|
@@ -101,9 +133,16 @@ module MU
|
|
|
101
133
|
found = {}
|
|
102
134
|
|
|
103
135
|
if args[:cloud_id]
|
|
104
|
-
arn =
|
|
105
|
-
|
|
106
|
-
|
|
136
|
+
arn = if args[:cloud_id].match(/^arn:/)
|
|
137
|
+
args[:cloud_id]
|
|
138
|
+
else
|
|
139
|
+
"arn:"+(MU::Cloud::AWS.isGovCloud?(args[:region]) ? "aws-us-gov" : "aws")+":sns:"+args[:region]+":"+MU::Cloud::AWS.credToAcct(args[:credentials])+":"+args[:cloud_id]
|
|
140
|
+
end
|
|
141
|
+
begin
|
|
142
|
+
desc = MU::Cloud::AWS.sns(region: args[:region], credentials: args[:credentials]).get_topic_attributes(topic_arn: arn).attributes
|
|
143
|
+
found[args[:cloud_id]] = desc if desc
|
|
144
|
+
rescue ::Aws::SNS::Errors::NotFound
|
|
145
|
+
end
|
|
107
146
|
else
|
|
108
147
|
next_token = nil
|
|
109
148
|
begin
|
|
@@ -120,6 +159,58 @@ module MU
|
|
|
120
159
|
found
|
|
121
160
|
end
|
|
122
161
|
|
|
162
|
+
# Reverse-map our cloud description into a runnable config hash.
|
|
163
|
+
# We assume that any values we have in +@config+ are placeholders, and
|
|
164
|
+
# calculate our own accordingly based on what's live in the cloud.
|
|
165
|
+
def toKitten(**_args)
|
|
166
|
+
bok = {
|
|
167
|
+
"cloud" => "AWS",
|
|
168
|
+
"credentials" => @credentials,
|
|
169
|
+
"cloud_id" => @cloud_id,
|
|
170
|
+
"region" => @region
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if !cloud_desc
|
|
174
|
+
MU.log "toKitten failed to load a cloud_desc from #{@cloud_id}", MU::ERR, details: @config
|
|
175
|
+
return nil
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
bok['name'] = cloud_desc["DisplayName"].empty? ? @cloud_id : cloud_desc["DisplayName"]
|
|
179
|
+
svcmap = {
|
|
180
|
+
"lambda" => "functions",
|
|
181
|
+
"sqs" => "msg_queues"
|
|
182
|
+
}
|
|
183
|
+
MU::Cloud::AWS.sns(region: @region, credentials: @credentials).list_subscriptions_by_topic(topic_arn: cloud_desc["TopicArn"]).subscriptions.each { |sub|
|
|
184
|
+
bok['subscriptions'] ||= []
|
|
185
|
+
|
|
186
|
+
bok['subscriptions'] << if sub.endpoint.match(/^arn:[^:]+:(sqs|lambda):([^:]+):(\d+):.*?([^:\/]+)$/)
|
|
187
|
+
_wholestring, service, region, account, id = Regexp.last_match.to_a
|
|
188
|
+
{
|
|
189
|
+
"type" => sub.protocol,
|
|
190
|
+
"resource" => MU::Config::Ref.get(
|
|
191
|
+
type: svcmap[service],
|
|
192
|
+
region: region,
|
|
193
|
+
credentials: @credentials,
|
|
194
|
+
id: id,
|
|
195
|
+
cloud: "AWS",
|
|
196
|
+
habitat: MU::Config::Ref.get(
|
|
197
|
+
id: account,
|
|
198
|
+
cloud: "AWS",
|
|
199
|
+
credentials: @credentials
|
|
200
|
+
)
|
|
201
|
+
)
|
|
202
|
+
}
|
|
203
|
+
else
|
|
204
|
+
{
|
|
205
|
+
"type" => sub.protocol,
|
|
206
|
+
"endpoint" => sub.endpoint
|
|
207
|
+
}
|
|
208
|
+
end
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
bok
|
|
212
|
+
end
|
|
213
|
+
|
|
123
214
|
# Cloud-specific configuration properties.
|
|
124
215
|
# @param _config [MU::Config]: The calling MU::Config object
|
|
125
216
|
# @return [Array<Array,Hash>]: List of required fields, and json-schema Hash of cloud-specific configuration parameters for this resource
|
|
@@ -130,11 +221,10 @@ module MU
|
|
|
130
221
|
"type" => "array",
|
|
131
222
|
"items" => {
|
|
132
223
|
"type" => "object",
|
|
133
|
-
"required" => ["endpoint"],
|
|
134
224
|
"properties" => {
|
|
135
225
|
"type" => {
|
|
136
226
|
"type" => "string",
|
|
137
|
-
"description" => "",
|
|
227
|
+
"description" => "Type of endpoint or resource which should receive notifications. If not specified, will attempt to auto-detect.",
|
|
138
228
|
"enum" => ["http", "https", "email", "email-json", "sms", "sqs", "application", "lambda"]
|
|
139
229
|
}
|
|
140
230
|
}
|
|
@@ -148,26 +238,42 @@ module MU
|
|
|
148
238
|
# Cloud-specific pre-processing of {MU::Config::BasketofKittens::notifier}, bare and unvalidated.
|
|
149
239
|
|
|
150
240
|
# @param notifier [Hash]: The resource to process and validate
|
|
151
|
-
# @param
|
|
241
|
+
# @param configurator [MU::Config]: The overall deployment configurator of which this resource is a member
|
|
152
242
|
# @return [Boolean]: True if validation succeeded, False otherwise
|
|
153
|
-
def self.validateConfig(notifier,
|
|
243
|
+
def self.validateConfig(notifier, configurator)
|
|
154
244
|
ok = true
|
|
155
245
|
|
|
156
246
|
if notifier['subscriptions']
|
|
157
247
|
notifier['subscriptions'].each { |sub|
|
|
248
|
+
if sub['resource'] and configurator.haveLitterMate?(sub['resource']['name'], sub['resource']['type'])
|
|
249
|
+
sub['resource']['cloud'] = "AWS"
|
|
250
|
+
MU::Config.addDependency(notifier, sub['resource']['name'], sub['resource']['type'])
|
|
251
|
+
end
|
|
158
252
|
if !sub["type"]
|
|
159
|
-
if sub[
|
|
160
|
-
sub[
|
|
161
|
-
|
|
162
|
-
sub[
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
elsif sub[
|
|
166
|
-
sub["
|
|
167
|
-
|
|
168
|
-
sub["
|
|
169
|
-
|
|
170
|
-
|
|
253
|
+
sub['type'] = if sub['resource']
|
|
254
|
+
if sub['resource']['type'] == "functions"
|
|
255
|
+
"lambda"
|
|
256
|
+
elsif sub['resource']['type'] == "msg_queues"
|
|
257
|
+
"sqs"
|
|
258
|
+
end
|
|
259
|
+
elsif sub['endpoint']
|
|
260
|
+
if sub["endpoint"].match(/^http:/i)
|
|
261
|
+
"http"
|
|
262
|
+
elsif sub["endpoint"].match(/^https:/i)
|
|
263
|
+
"https"
|
|
264
|
+
elsif sub["endpoint"].match(/:sqs:/i)
|
|
265
|
+
"sqs"
|
|
266
|
+
elsif sub["endpoint"].match(/:lambda:/i)
|
|
267
|
+
"lambda"
|
|
268
|
+
elsif sub["endpoint"].match(/^\+?[\d\-]+$/)
|
|
269
|
+
"sms"
|
|
270
|
+
elsif sub["endpoint"].match(/\A[\w+\-.]+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i)
|
|
271
|
+
"email"
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
if !sub['type']
|
|
276
|
+
MU.log "Notifier #{notifier['name']} subscription did not specify a type, and I'm unable to guess one", MU::ERR, details: sub
|
|
171
277
|
ok = false
|
|
172
278
|
end
|
|
173
279
|
end
|
|
@@ -178,40 +284,6 @@ module MU
|
|
|
178
284
|
end
|
|
179
285
|
|
|
180
286
|
|
|
181
|
-
# Subscribe to a notifier group. This can either be an email address, SQS queue, application endpoint, etc...
|
|
182
|
-
# Will create the subscription only if it doesn't already exist.
|
|
183
|
-
# @param arn [String]: The cloud provider's identifier of the notifier group.
|
|
184
|
-
# @param protocol [String]: The type of the subscription (eg. email,https, etc..).
|
|
185
|
-
# @param endpoint [String]: The endpoint of the subscription. This will depend on the 'protocol' (as an example if protocol is email, endpoint will be the email address) ..
|
|
186
|
-
# @param region [String]: The cloud provider region.
|
|
187
|
-
def self.subscribe(arn: nil, protocol: nil, endpoint: nil, region: MU.curRegion, credentials: nil)
|
|
188
|
-
retries = 0
|
|
189
|
-
begin
|
|
190
|
-
resp = MU::Cloud::AWS.sns(region: region, credentials: credentials).list_subscriptions_by_topic(topic_arn: arn).subscriptions
|
|
191
|
-
rescue Aws::SNS::Errors::NotFound
|
|
192
|
-
if retries < 5
|
|
193
|
-
MU.log "Couldn't find topic #{arn}, retrying several times in case of a lagging resource"
|
|
194
|
-
retries += 1
|
|
195
|
-
sleep 30
|
|
196
|
-
retry
|
|
197
|
-
else
|
|
198
|
-
raise MuError, "Couldn't find topic #{arn}, giving up"
|
|
199
|
-
end
|
|
200
|
-
end
|
|
201
|
-
|
|
202
|
-
already_subscribed = false
|
|
203
|
-
if resp && !resp.empty?
|
|
204
|
-
resp.each { |subscription|
|
|
205
|
-
already_subscribed = true if subscription.protocol == protocol && subscription.endpoint == endpoint
|
|
206
|
-
}
|
|
207
|
-
end
|
|
208
|
-
|
|
209
|
-
unless already_subscribed
|
|
210
|
-
MU::Cloud::AWS.sns(region: region, credentials: credentials).subscribe(topic_arn: arn, protocol: protocol, endpoint: endpoint)
|
|
211
|
-
MU.log "Subscribed #{endpoint} to SNS topic #{arn}"
|
|
212
|
-
end
|
|
213
|
-
end
|
|
214
|
-
|
|
215
287
|
# Test if a notifier group exists
|
|
216
288
|
# Create a new notifier group. Will check if the group exists before creating it.
|
|
217
289
|
# @param topic_name [String]: The cloud provider's name for the notifier group.
|