cloud-mu 3.1.3 → 3.3.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 +15 -3
- data/ansible/roles/mu-windows/README.md +33 -0
- data/ansible/roles/mu-windows/defaults/main.yml +2 -0
- data/ansible/roles/mu-windows/files/LaunchConfig.json +9 -0
- data/ansible/roles/mu-windows/files/config.xml +76 -0
- data/ansible/roles/mu-windows/handlers/main.yml +2 -0
- data/ansible/roles/mu-windows/meta/main.yml +53 -0
- data/ansible/roles/mu-windows/tasks/main.yml +36 -0
- data/ansible/roles/mu-windows/tests/inventory +2 -0
- data/ansible/roles/mu-windows/tests/test.yml +5 -0
- data/ansible/roles/mu-windows/vars/main.yml +2 -0
- data/bin/mu-adopt +21 -13
- data/bin/mu-azure-tests +57 -0
- data/bin/mu-cleanup +2 -4
- data/bin/mu-configure +52 -0
- data/bin/mu-deploy +3 -3
- data/bin/mu-findstray-tests +25 -0
- data/bin/mu-gen-docs +2 -4
- data/bin/mu-load-config.rb +4 -4
- data/bin/mu-node-manage +15 -16
- data/bin/mu-run-tests +147 -37
- data/cloud-mu.gemspec +22 -20
- data/cookbooks/mu-activedirectory/resources/domain.rb +4 -4
- data/cookbooks/mu-activedirectory/resources/domain_controller.rb +4 -4
- data/cookbooks/mu-tools/libraries/helper.rb +3 -2
- data/cookbooks/mu-tools/libraries/monkey.rb +35 -0
- data/cookbooks/mu-tools/recipes/apply_security.rb +14 -14
- data/cookbooks/mu-tools/recipes/aws_api.rb +9 -0
- data/cookbooks/mu-tools/recipes/eks.rb +2 -2
- data/cookbooks/mu-tools/recipes/google_api.rb +2 -2
- data/cookbooks/mu-tools/recipes/selinux.rb +2 -1
- data/cookbooks/mu-tools/recipes/windows-client.rb +163 -164
- data/cookbooks/mu-tools/resources/disk.rb +1 -1
- data/cookbooks/mu-tools/resources/windows_users.rb +44 -43
- data/extras/clean-stock-amis +25 -19
- data/extras/generate-stock-images +1 -0
- data/extras/image-generators/AWS/win2k12.yaml +18 -13
- data/extras/image-generators/AWS/win2k16.yaml +18 -13
- data/extras/image-generators/AWS/win2k19.yaml +21 -0
- data/extras/image-generators/Google/centos6.yaml +1 -0
- data/extras/image-generators/Google/centos7.yaml +1 -1
- data/modules/mommacat.ru +6 -16
- data/modules/mu.rb +158 -111
- data/modules/mu/adoption.rb +404 -71
- data/modules/mu/cleanup.rb +221 -306
- data/modules/mu/cloud.rb +129 -1633
- data/modules/mu/cloud/database.rb +49 -0
- data/modules/mu/cloud/dnszone.rb +44 -0
- data/modules/mu/cloud/machine_images.rb +212 -0
- data/modules/mu/cloud/providers.rb +81 -0
- data/modules/mu/cloud/resource_base.rb +926 -0
- data/modules/mu/cloud/server.rb +40 -0
- data/modules/mu/cloud/server_pool.rb +1 -0
- data/modules/mu/cloud/ssh_sessions.rb +228 -0
- data/modules/mu/cloud/winrm_sessions.rb +237 -0
- data/modules/mu/cloud/wrappers.rb +169 -0
- data/modules/mu/config.rb +171 -1767
- data/modules/mu/config/alarm.rb +2 -6
- data/modules/mu/config/bucket.rb +32 -3
- data/modules/mu/config/cache_cluster.rb +2 -2
- data/modules/mu/config/cdn.rb +100 -0
- data/modules/mu/config/collection.rb +4 -4
- data/modules/mu/config/container_cluster.rb +9 -4
- data/modules/mu/config/database.rb +84 -105
- data/modules/mu/config/database.yml +1 -2
- data/modules/mu/config/dnszone.rb +10 -9
- data/modules/mu/config/doc_helpers.rb +516 -0
- data/modules/mu/config/endpoint.rb +5 -4
- data/modules/mu/config/firewall_rule.rb +103 -4
- data/modules/mu/config/folder.rb +4 -4
- data/modules/mu/config/function.rb +19 -10
- data/modules/mu/config/group.rb +4 -4
- data/modules/mu/config/habitat.rb +4 -4
- data/modules/mu/config/job.rb +89 -0
- data/modules/mu/config/loadbalancer.rb +60 -14
- data/modules/mu/config/log.rb +4 -4
- data/modules/mu/config/msg_queue.rb +4 -4
- data/modules/mu/config/nosqldb.rb +4 -4
- data/modules/mu/config/notifier.rb +10 -21
- data/modules/mu/config/ref.rb +411 -0
- data/modules/mu/config/role.rb +4 -4
- data/modules/mu/config/schema_helpers.rb +509 -0
- data/modules/mu/config/search_domain.rb +4 -4
- data/modules/mu/config/server.rb +98 -71
- data/modules/mu/config/server.yml +1 -0
- data/modules/mu/config/server_pool.rb +5 -9
- data/modules/mu/config/storage_pool.rb +1 -1
- data/modules/mu/config/tail.rb +200 -0
- data/modules/mu/config/user.rb +4 -4
- data/modules/mu/config/vpc.rb +71 -27
- data/modules/mu/config/vpc.yml +0 -1
- data/modules/mu/defaults/AWS.yaml +91 -68
- data/modules/mu/defaults/Azure.yaml +1 -0
- data/modules/mu/defaults/Google.yaml +3 -2
- data/modules/mu/deploy.rb +43 -26
- data/modules/mu/groomer.rb +17 -2
- data/modules/mu/groomers/ansible.rb +188 -41
- data/modules/mu/groomers/chef.rb +116 -55
- data/modules/mu/logger.rb +127 -148
- data/modules/mu/master.rb +410 -2
- data/modules/mu/master/chef.rb +3 -4
- data/modules/mu/master/ldap.rb +3 -3
- data/modules/mu/master/ssl.rb +12 -3
- data/modules/mu/mommacat.rb +218 -2612
- data/modules/mu/mommacat/daemon.rb +403 -0
- data/modules/mu/mommacat/naming.rb +473 -0
- data/modules/mu/mommacat/search.rb +495 -0
- data/modules/mu/mommacat/storage.rb +722 -0
- data/modules/mu/{clouds → providers}/README.md +1 -1
- data/modules/mu/{clouds → providers}/aws.rb +380 -122
- data/modules/mu/{clouds → providers}/aws/alarm.rb +7 -5
- data/modules/mu/{clouds → providers}/aws/bucket.rb +297 -59
- data/modules/mu/{clouds → providers}/aws/cache_cluster.rb +37 -71
- data/modules/mu/providers/aws/cdn.rb +782 -0
- data/modules/mu/{clouds → providers}/aws/collection.rb +26 -25
- data/modules/mu/{clouds → providers}/aws/container_cluster.rb +724 -744
- data/modules/mu/providers/aws/database.rb +1744 -0
- data/modules/mu/{clouds → providers}/aws/dnszone.rb +88 -70
- data/modules/mu/providers/aws/endpoint.rb +1072 -0
- data/modules/mu/{clouds → providers}/aws/firewall_rule.rb +220 -247
- data/modules/mu/{clouds → providers}/aws/folder.rb +8 -8
- data/modules/mu/{clouds → providers}/aws/function.rb +300 -142
- data/modules/mu/{clouds → providers}/aws/group.rb +31 -29
- data/modules/mu/{clouds → providers}/aws/habitat.rb +18 -15
- data/modules/mu/providers/aws/job.rb +466 -0
- data/modules/mu/{clouds → providers}/aws/loadbalancer.rb +66 -56
- data/modules/mu/{clouds → providers}/aws/log.rb +17 -14
- data/modules/mu/{clouds → providers}/aws/msg_queue.rb +29 -19
- data/modules/mu/{clouds → providers}/aws/nosqldb.rb +114 -16
- data/modules/mu/{clouds → providers}/aws/notifier.rb +142 -65
- data/modules/mu/{clouds → providers}/aws/role.rb +158 -118
- data/modules/mu/{clouds → providers}/aws/search_domain.rb +201 -59
- data/modules/mu/{clouds → providers}/aws/server.rb +844 -1139
- data/modules/mu/{clouds → providers}/aws/server_pool.rb +74 -65
- data/modules/mu/{clouds → providers}/aws/storage_pool.rb +26 -44
- data/modules/mu/{clouds → providers}/aws/user.rb +24 -25
- data/modules/mu/{clouds → providers}/aws/userdata/README.md +0 -0
- data/modules/mu/{clouds → providers}/aws/userdata/linux.erb +5 -4
- data/modules/mu/{clouds → providers}/aws/userdata/windows.erb +2 -1
- data/modules/mu/{clouds → providers}/aws/vpc.rb +525 -931
- data/modules/mu/providers/aws/vpc_subnet.rb +286 -0
- data/modules/mu/{clouds → providers}/azure.rb +29 -9
- data/modules/mu/{clouds → providers}/azure/container_cluster.rb +3 -8
- data/modules/mu/{clouds → providers}/azure/firewall_rule.rb +18 -11
- data/modules/mu/{clouds → providers}/azure/habitat.rb +8 -6
- data/modules/mu/{clouds → providers}/azure/loadbalancer.rb +5 -5
- data/modules/mu/{clouds → providers}/azure/role.rb +8 -10
- data/modules/mu/{clouds → providers}/azure/server.rb +97 -49
- data/modules/mu/{clouds → providers}/azure/user.rb +6 -8
- data/modules/mu/{clouds → providers}/azure/userdata/README.md +0 -0
- data/modules/mu/{clouds → providers}/azure/userdata/linux.erb +0 -0
- data/modules/mu/{clouds → providers}/azure/userdata/windows.erb +0 -0
- data/modules/mu/{clouds → providers}/azure/vpc.rb +16 -21
- data/modules/mu/{clouds → providers}/cloudformation.rb +18 -7
- data/modules/mu/{clouds → providers}/cloudformation/alarm.rb +3 -3
- data/modules/mu/{clouds → providers}/cloudformation/cache_cluster.rb +3 -3
- data/modules/mu/{clouds → providers}/cloudformation/collection.rb +3 -3
- data/modules/mu/{clouds → providers}/cloudformation/database.rb +6 -17
- data/modules/mu/{clouds → providers}/cloudformation/dnszone.rb +3 -3
- data/modules/mu/{clouds → providers}/cloudformation/firewall_rule.rb +3 -3
- data/modules/mu/{clouds → providers}/cloudformation/loadbalancer.rb +3 -3
- data/modules/mu/{clouds → providers}/cloudformation/log.rb +3 -3
- data/modules/mu/{clouds → providers}/cloudformation/server.rb +7 -7
- data/modules/mu/{clouds → providers}/cloudformation/server_pool.rb +5 -5
- data/modules/mu/{clouds → providers}/cloudformation/vpc.rb +5 -7
- data/modules/mu/{clouds → providers}/docker.rb +0 -0
- data/modules/mu/{clouds → providers}/google.rb +68 -30
- data/modules/mu/{clouds → providers}/google/bucket.rb +13 -15
- data/modules/mu/{clouds → providers}/google/container_cluster.rb +85 -78
- data/modules/mu/{clouds → providers}/google/database.rb +11 -21
- data/modules/mu/{clouds → providers}/google/firewall_rule.rb +15 -14
- data/modules/mu/{clouds → providers}/google/folder.rb +20 -17
- data/modules/mu/{clouds → providers}/google/function.rb +140 -168
- data/modules/mu/{clouds → providers}/google/group.rb +29 -34
- data/modules/mu/{clouds → providers}/google/habitat.rb +21 -22
- data/modules/mu/{clouds → providers}/google/loadbalancer.rb +19 -21
- data/modules/mu/{clouds → providers}/google/role.rb +94 -58
- data/modules/mu/{clouds → providers}/google/server.rb +243 -156
- data/modules/mu/{clouds → providers}/google/server_pool.rb +26 -45
- data/modules/mu/{clouds → providers}/google/user.rb +95 -31
- data/modules/mu/{clouds → providers}/google/userdata/README.md +0 -0
- data/modules/mu/{clouds → providers}/google/userdata/linux.erb +0 -0
- data/modules/mu/{clouds → providers}/google/userdata/windows.erb +0 -0
- data/modules/mu/{clouds → providers}/google/vpc.rb +103 -79
- data/modules/tests/aws-jobs-functions.yaml +46 -0
- data/modules/tests/bucket.yml +4 -0
- data/modules/tests/centos6.yaml +15 -0
- data/modules/tests/centos7.yaml +15 -0
- data/modules/tests/centos8.yaml +12 -0
- data/modules/tests/ecs.yaml +23 -0
- 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/includes-and-params.yaml +2 -1
- data/modules/tests/microservice_app.yaml +288 -0
- data/modules/tests/rds.yaml +108 -0
- data/modules/tests/regrooms/aws-iam.yaml +201 -0
- data/modules/tests/regrooms/bucket.yml +19 -0
- data/modules/tests/regrooms/rds.yaml +123 -0
- data/modules/tests/server-with-scrub-muisms.yaml +2 -1
- data/modules/tests/super_complex_bok.yml +2 -2
- data/modules/tests/super_simple_bok.yml +3 -5
- data/modules/tests/win2k12.yaml +17 -5
- data/modules/tests/win2k16.yaml +25 -0
- data/modules/tests/win2k19.yaml +25 -0
- data/requirements.txt +1 -0
- data/spec/mu/clouds/azure_spec.rb +2 -2
- metadata +240 -154
- data/extras/image-generators/AWS/windows.yaml +0 -18
- data/modules/mu/clouds/aws/database.rb +0 -1985
- data/modules/mu/clouds/aws/endpoint.rb +0 -592
|
@@ -61,7 +61,7 @@ module MU
|
|
|
61
61
|
]
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
keypairname,
|
|
64
|
+
keypairname, _ssh_private_key, _ssh_public_key = @deploy.SSHKey
|
|
65
65
|
|
|
66
66
|
parameters = Array.new
|
|
67
67
|
if !@config["parameters"].nil?
|
|
@@ -108,7 +108,7 @@ module MU
|
|
|
108
108
|
end
|
|
109
109
|
|
|
110
110
|
MU.log "Creating CloudFormation stack '#{@config['name']}'", details: stack_descriptor
|
|
111
|
-
|
|
111
|
+
MU::Cloud::AWS.cloudformation(region: region, credentials: @config['credentials']).create_stack(stack_descriptor);
|
|
112
112
|
|
|
113
113
|
sleep(10);
|
|
114
114
|
stack_response = MU::Cloud::AWS.cloudformation(region: region, credentials: @config['credentials']).describe_stacks({:stack_name => stack_name}).stacks.first
|
|
@@ -135,7 +135,7 @@ module MU
|
|
|
135
135
|
end
|
|
136
136
|
|
|
137
137
|
if flag == "FAIL" then
|
|
138
|
-
|
|
138
|
+
MU::Cloud::AWS.cloudformation(region: region, credentials: @config['credentials']).delete_stack({:stack_name => stack_name})
|
|
139
139
|
exit 1
|
|
140
140
|
end
|
|
141
141
|
|
|
@@ -150,14 +150,14 @@ module MU
|
|
|
150
150
|
when "AWS::EC2::Instance"
|
|
151
151
|
MU::Cloud::AWS.createStandardTags(resource.physical_resource_id)
|
|
152
152
|
instance_name = MU.deploy_id+"-"+@config['name']+"-"+resource.logical_resource_id
|
|
153
|
-
MU::
|
|
153
|
+
MU::Cloud::AWS.createTag(resource.physical_resource_id, "Name", instance_name, credentials: @config['credentials'])
|
|
154
154
|
|
|
155
|
-
instance = MU::Cloud
|
|
155
|
+
instance = MU::Cloud.resourceClass("AWS", "Server").notifyDeploy(
|
|
156
156
|
@config['name']+"-"+resource.logical_resource_id,
|
|
157
157
|
resource.physical_resource_id
|
|
158
158
|
)
|
|
159
159
|
|
|
160
|
-
MU::
|
|
160
|
+
MU::Master.addHostToSSHConfig(
|
|
161
161
|
instance_name,
|
|
162
162
|
instance["private_ip_address"],
|
|
163
163
|
instance["private_dns_name"],
|
|
@@ -168,23 +168,23 @@ module MU
|
|
|
168
168
|
key_name: instance["key_name"]
|
|
169
169
|
)
|
|
170
170
|
|
|
171
|
-
mu_zone,
|
|
171
|
+
mu_zone, _junk = MU::Cloud::DNSZone.find(name: "mu")
|
|
172
172
|
if !mu_zone.nil?
|
|
173
|
-
MU::Cloud
|
|
173
|
+
MU::Cloud.resourceClass("AWS", "DNSZone").genericMuDNSEntry(instance_name, instance["private_ip_address"], MU::Cloud::Server)
|
|
174
174
|
else
|
|
175
|
-
MU::
|
|
175
|
+
MU::Master.addInstanceToEtcHosts(instance["public_ip_address"], instance_name)
|
|
176
176
|
end
|
|
177
177
|
|
|
178
178
|
when "AWS::EC2::SecurityGroup"
|
|
179
179
|
MU::Cloud::AWS.createStandardTags(resource.physical_resource_id)
|
|
180
|
-
MU::
|
|
181
|
-
MU::Cloud
|
|
180
|
+
MU::Cloud::AWS.createTag(resource.physical_resource_id, "Name", MU.deploy_id+"-"+@config['name']+'-'+resource.logical_resource_id, credentials: @config['credentials'])
|
|
181
|
+
MU::Cloud.resourceClass("AWS", "FirewallRule").notifyDeploy(
|
|
182
182
|
@config['name']+"-"+resource.logical_resource_id,
|
|
183
183
|
resource.physical_resource_id
|
|
184
184
|
)
|
|
185
185
|
when "AWS::EC2::Subnet"
|
|
186
186
|
MU::Cloud::AWS.createStandardTags(resource.physical_resource_id)
|
|
187
|
-
MU::
|
|
187
|
+
MU::Cloud::AWS.createTag(resource.physical_resource_id, "Name", MU.deploy_id+"-"+@config['name']+'-'+resource.logical_resource_id, credentials: @config['credentials'])
|
|
188
188
|
data = {
|
|
189
189
|
"collection" => @config["name"],
|
|
190
190
|
"subnet_id" => resource.physical_resource_id,
|
|
@@ -192,7 +192,7 @@ module MU
|
|
|
192
192
|
@deploy.notify("subnets", @config['name']+"-"+resource.logical_resource_id, data)
|
|
193
193
|
when "AWS::EC2::VPC"
|
|
194
194
|
MU::Cloud::AWS.createStandardTags(resource.physical_resource_id)
|
|
195
|
-
MU::
|
|
195
|
+
MU::Cloud::AWS.createTag(resource.physical_resource_id, "Name", MU.deploy_id+"-"+@config['name']+'-'+resource.logical_resource_id, credentials: @config['credentials'])
|
|
196
196
|
data = {
|
|
197
197
|
"collection" => @config["name"],
|
|
198
198
|
"vpc_id" => resource.physical_resource_id,
|
|
@@ -200,10 +200,10 @@ module MU
|
|
|
200
200
|
@deploy.notify("vpcs", @config['name']+"-"+resource.logical_resource_id, data)
|
|
201
201
|
when "AWS::EC2::InternetGateway"
|
|
202
202
|
MU::Cloud::AWS.createStandardTags(resource.physical_resource_id)
|
|
203
|
-
MU::
|
|
203
|
+
MU::Cloud::AWS.createTag(resource.physical_resource_id, "Name", MU.deploy_id+"-"+@config['name']+'-'+resource.logical_resource_id, credentials: @config['credentials'])
|
|
204
204
|
when "AWS::EC2::RouteTable"
|
|
205
205
|
MU::Cloud::AWS.createStandardTags(resource.physical_resource_id)
|
|
206
|
-
MU::
|
|
206
|
+
MU::Cloud::AWS.createTag(resource.physical_resource_id, "Name", MU.deploy_id+"-"+@config['name']+'-'+resource.logical_resource_id, credentials: @config['credentials'])
|
|
207
207
|
|
|
208
208
|
# The rest of these aren't anything we act on
|
|
209
209
|
when "AWS::EC2::Route"
|
|
@@ -242,13 +242,16 @@ module MU
|
|
|
242
242
|
# @param region [String]: The cloud provider region
|
|
243
243
|
# @param wait [Boolean]: Block on the removal of this stack; AWS deletion will continue in the background otherwise if false.
|
|
244
244
|
# @return [void]
|
|
245
|
-
def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, wait: false, credentials: nil, flags: {})
|
|
245
|
+
def self.cleanup(noop: false, deploy_id: MU.deploy_id, ignoremaster: false, region: MU.curRegion, wait: false, credentials: nil, flags: {})
|
|
246
|
+
MU.log "AWS::Collection.cleanup: need to support flags['known']", MU::DEBUG, details: flags
|
|
247
|
+
MU.log "Placeholder: AWS Collection artifacts do not support tags, so ignoremaster cleanup flag has no effect", MU::DEBUG, details: ignoremaster
|
|
248
|
+
|
|
246
249
|
# XXX needs to check tags instead of name- possible?
|
|
247
250
|
resp = MU::Cloud::AWS.cloudformation(credentials: credentials, region: region).describe_stacks
|
|
248
251
|
resp.stacks.each { |stack|
|
|
249
252
|
ok = false
|
|
250
253
|
stack.tags.each { |tag|
|
|
251
|
-
ok = true if (tag.key == "MU-ID") and tag.value ==
|
|
254
|
+
ok = true if (tag.key == "MU-ID") and tag.value == deploy_id
|
|
252
255
|
}
|
|
253
256
|
if ok
|
|
254
257
|
MU.log "Deleting CloudFormation stack #{stack.stack_name})"
|
|
@@ -257,7 +260,6 @@ module MU
|
|
|
257
260
|
MU::Cloud::AWS.cloudformation(credentials: credentials, region: region).delete_stack(stack_name: stack.stack_name)
|
|
258
261
|
end
|
|
259
262
|
if wait
|
|
260
|
-
last_status = ""
|
|
261
263
|
max_retries = 10
|
|
262
264
|
retries = 0
|
|
263
265
|
mystack = nil
|
|
@@ -272,10 +274,9 @@ module MU
|
|
|
272
274
|
MU.log "Couldn't delete CloudFormation stack #{stack.stack_name}", MU::ERR, details: mystack.stack_status_reason
|
|
273
275
|
return
|
|
274
276
|
end
|
|
275
|
-
last_status = mystack.stack_status_reason
|
|
276
277
|
MU.log "Waiting for CloudFormation stack #{stack.stack_name} to delete (#{stack.stack_status})...", MU::NOTICE
|
|
277
278
|
end
|
|
278
|
-
rescue Aws::CloudFormation::Errors::ValidationError
|
|
279
|
+
rescue Aws::CloudFormation::Errors::ValidationError
|
|
279
280
|
# this is ok, it means deletion finally succeeded
|
|
280
281
|
|
|
281
282
|
end while !desc.nil? and desc.size > 0 and retries < max_retries
|
|
@@ -317,19 +318,19 @@ module MU
|
|
|
317
318
|
end
|
|
318
319
|
|
|
319
320
|
# Cloud-specific configuration properties.
|
|
320
|
-
# @param
|
|
321
|
+
# @param _config [MU::Config]: The calling MU::Config object
|
|
321
322
|
# @return [Array<Array,Hash>]: List of required fields, and json-schema Hash of cloud-specific configuration parameters for this resource
|
|
322
|
-
def self.schema(
|
|
323
|
+
def self.schema(_config)
|
|
323
324
|
toplevel_required = []
|
|
324
325
|
schema = {}
|
|
325
326
|
[toplevel_required, schema]
|
|
326
327
|
end
|
|
327
328
|
|
|
328
329
|
# Cloud-specific pre-processing of {MU::Config::BasketofKittens::collections}, bare and unvalidated.
|
|
329
|
-
# @param
|
|
330
|
-
# @param
|
|
330
|
+
# @param _stack [Hash]: The resource to process and validate
|
|
331
|
+
# @param _configurator [MU::Config]: The overall deployment configurator of which this resource is a member
|
|
331
332
|
# @return [Boolean]: True if validation succeeded, False otherwise
|
|
332
|
-
def self.validateConfig(
|
|
333
|
+
def self.validateConfig(_stack, _configurator)
|
|
333
334
|
true
|
|
334
335
|
end
|
|
335
336
|
|
|
@@ -39,123 +39,81 @@ module MU
|
|
|
39
39
|
def create
|
|
40
40
|
if @config['flavor'] == "EKS" or
|
|
41
41
|
(@config['flavor'] == "Fargate" and !@config['containers'])
|
|
42
|
-
subnet_ids = []
|
|
43
|
-
@config["vpc"]["subnets"].each { |subnet|
|
|
44
|
-
subnet_obj = @vpc.getSubnet(cloud_id: subnet["subnet_id"].to_s, name: subnet["subnet_name"].to_s)
|
|
45
|
-
raise MuError, "Couldn't find a live subnet matching #{subnet} in #{@vpc} (#{@vpc.subnets})" if subnet_obj.nil?
|
|
46
|
-
subnet_ids << subnet_obj.cloud_id
|
|
47
|
-
}
|
|
48
42
|
|
|
49
|
-
|
|
43
|
+
subnet_ids = mySubnets.map { |s| s.cloud_id }
|
|
50
44
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
@
|
|
54
|
-
|
|
45
|
+
params = {
|
|
46
|
+
:name => @mu_name,
|
|
47
|
+
:version => @config['kubernetes']['version'],
|
|
48
|
+
:role_arn => @deploy.findLitterMate(name: @config['name']+"controlplane", type: "roles").arn,
|
|
49
|
+
:resources_vpc_config => {
|
|
50
|
+
:security_group_ids => myFirewallRules.map { |fw| fw.cloud_id },
|
|
51
|
+
:subnet_ids => subnet_ids
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if @config['logging'] and @config['logging'].size > 0
|
|
55
|
+
params[:logging] = {
|
|
56
|
+
:cluster_logging => [
|
|
57
|
+
{
|
|
58
|
+
:types => @config['logging'],
|
|
59
|
+
:enabled => true
|
|
60
|
+
}
|
|
61
|
+
]
|
|
55
62
|
}
|
|
56
63
|
end
|
|
64
|
+
params.delete(:version) if params[:version] == "latest"
|
|
57
65
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
:security_group_ids => security_groups,
|
|
66
|
-
:subnet_ids => subnet_ids
|
|
66
|
+
on_retry = Proc.new { |e|
|
|
67
|
+
# soul-crushing, yet effective
|
|
68
|
+
if e.message.match(/because (#{Regexp.quote(@config['region'])}[a-z]), the targeted availability zone, does not currently have sufficient capacity/)
|
|
69
|
+
bad_az = Regexp.last_match(1)
|
|
70
|
+
deletia = []
|
|
71
|
+
mySubnets.each { |subnet|
|
|
72
|
+
deletia << subnet.cloud_id if subnet.az == bad_az
|
|
67
73
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
:
|
|
72
|
-
{
|
|
73
|
-
:types => @config['logging'],
|
|
74
|
-
:enabled => true
|
|
75
|
-
}
|
|
76
|
-
]
|
|
74
|
+
raise e if deletia.empty?
|
|
75
|
+
MU.log "#{bad_az} does not have EKS capacity. Dropping unsupported subnets from ContainerCluster '#{@config['name']}' and retrying.", MU::NOTICE, details: deletia
|
|
76
|
+
deletia.each { |subnet|
|
|
77
|
+
params[:resources_vpc_config][:subnet_ids].delete(subnet)
|
|
77
78
|
}
|
|
78
79
|
end
|
|
79
|
-
|
|
80
|
+
}
|
|
80
81
|
|
|
82
|
+
MU.retrier([Aws::EKS::Errors::UnsupportedAvailabilityZoneException, Aws::EKS::Errors::InvalidParameterException], on_retry: on_retry, max: subnet_ids.size) {
|
|
81
83
|
MU.log "Creating EKS cluster #{@mu_name}", details: params
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
if e.message.match(/because (#{Regexp.quote(@config['region'])}[a-z]), the targeted availability zone, does not currently have sufficient capacity/)
|
|
86
|
-
bad_az = Regexp.last_match(1)
|
|
87
|
-
deletia = nil
|
|
88
|
-
subnet_ids.each { |subnet|
|
|
89
|
-
subnet_obj = @vpc.getSubnet(cloud_id: subnet)
|
|
90
|
-
if subnet_obj.az == bad_az
|
|
91
|
-
deletia = subnet
|
|
92
|
-
break
|
|
93
|
-
end
|
|
94
|
-
}
|
|
95
|
-
raise e if deletia.nil?
|
|
96
|
-
MU.log "#{bad_az} does not have EKS capacity. Dropping #{deletia} from ContainerCluster '#{@config['name']}' and retrying.", MU::NOTICE
|
|
97
|
-
subnet_ids.delete(deletia)
|
|
98
|
-
retry
|
|
99
|
-
end
|
|
100
|
-
rescue Aws::EKS::Errors::InvalidParameterException => e
|
|
101
|
-
if e.message.match(/role with arn: #{Regexp.quote(role_arn)}.*?(could not be assumed|does not exist)/i)
|
|
102
|
-
sleep 5
|
|
103
|
-
retry
|
|
104
|
-
else
|
|
105
|
-
MU.log e.message, MU::WARN, details: params
|
|
106
|
-
sleep 5
|
|
107
|
-
retry
|
|
108
|
-
end
|
|
109
|
-
end
|
|
84
|
+
MU::Cloud::AWS.eks(region: @config['region'], credentials: @config['credentials']).create_cluster(params)
|
|
85
|
+
}
|
|
86
|
+
@cloud_id = @mu_name
|
|
110
87
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
status = resp.cluster.status
|
|
118
|
-
if status == "FAILED"
|
|
88
|
+
loop_if = Proc.new {
|
|
89
|
+
cloud_desc(use_cache: false).status != "ACTIVE"
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
MU.retrier(ignoreme: [Aws::EKS::Errors::ResourceNotFoundException], wait: 30, max: 60, loop_if: loop_if) { |retries, _wait|
|
|
93
|
+
if cloud_desc.status == "FAILED"
|
|
119
94
|
raise MuError, "EKS cluster #{@mu_name} had FAILED status"
|
|
120
95
|
end
|
|
121
|
-
if retries > 0 and (retries % 3) == 0 and status != "ACTIVE"
|
|
122
|
-
MU.log "Waiting for EKS cluster #{@mu_name} to become active (currently #{status})", MU::NOTICE
|
|
123
|
-
end
|
|
124
|
-
sleep 30
|
|
125
|
-
retries += 1
|
|
126
|
-
rescue Aws::EKS::Errors::ResourceNotFoundException => e
|
|
127
|
-
if retries < 30
|
|
128
|
-
if retries > 0 and (retries % 3) == 0
|
|
129
|
-
MU.log "Got #{e.message} trying to describe EKS cluster #{@mu_name}, waiting and retrying", MU::WARN, details: resp
|
|
130
|
-
end
|
|
131
|
-
sleep 30
|
|
132
|
-
retries += 1
|
|
133
|
-
retry
|
|
134
|
-
else
|
|
135
|
-
raise e
|
|
96
|
+
if retries > 0 and (retries % 3) == 0 and cloud_desc.status != "ACTIVE"
|
|
97
|
+
MU.log "Waiting for EKS cluster #{@mu_name} to become active (currently #{cloud_desc.status})", MU::NOTICE
|
|
136
98
|
end
|
|
137
|
-
|
|
99
|
+
}
|
|
138
100
|
|
|
139
101
|
MU.log "Creation of EKS cluster #{@mu_name} complete"
|
|
140
102
|
else
|
|
141
103
|
MU::Cloud::AWS.ecs(region: @config['region'], credentials: @config['credentials']).create_cluster(
|
|
142
104
|
cluster_name: @mu_name
|
|
143
105
|
)
|
|
144
|
-
|
|
106
|
+
@cloud_id = @mu_name
|
|
145
107
|
end
|
|
146
|
-
@cloud_id = @mu_name
|
|
147
108
|
end
|
|
148
109
|
|
|
149
110
|
# Called automatically by {MU::Deploy#createResources}
|
|
150
111
|
def groom
|
|
151
112
|
|
|
152
|
-
|
|
153
|
-
@deploy.findLitterMate(type: "server_pools", name: @config["name"]+"workers")
|
|
154
|
-
end
|
|
155
|
-
resource_lookup = MU::Cloud::AWS.listInstanceTypes(@config['region'])[@config['region']]
|
|
156
|
-
|
|
113
|
+
# EKS or Fargate-EKS: do Kubernetes things
|
|
157
114
|
if @config['flavor'] == "EKS" or
|
|
158
115
|
(@config['flavor'] == "Fargate" and !@config['containers'])
|
|
116
|
+
|
|
159
117
|
# This will be needed if a loadbalancer has never been created in
|
|
160
118
|
# this account; EKS applications might want one, but will fail in
|
|
161
119
|
# confusing ways if this hasn't been done.
|
|
@@ -166,228 +124,20 @@ module MU
|
|
|
166
124
|
rescue ::Aws::IAM::Errors::InvalidInput
|
|
167
125
|
end
|
|
168
126
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
tagme_elb = []
|
|
173
|
-
@vpc.subnets.each { |s|
|
|
174
|
-
tagme << s.cloud_id
|
|
175
|
-
tagme_elb << s.cloud_id if !s.private?
|
|
176
|
-
}
|
|
177
|
-
rtbs = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).describe_route_tables(
|
|
178
|
-
filters: [ { name: "vpc-id", values: [@vpc.cloud_id] } ]
|
|
179
|
-
).route_tables
|
|
180
|
-
tagme.concat(rtbs.map { |r| r.route_table_id } )
|
|
181
|
-
main_sg = @deploy.findLitterMate(type: "firewall_rules", name: "server_pool#{@config['name']}workers")
|
|
182
|
-
tagme << main_sg.cloud_id if main_sg
|
|
183
|
-
MU.log "Applying kubernetes.io tags to VPC resources", details: tagme
|
|
184
|
-
MU::Cloud::AWS.createTag("kubernetes.io/cluster/#{@mu_name}", "shared", tagme, credentials: @config['credentials'])
|
|
185
|
-
MU::Cloud::AWS.createTag("kubernetes.io/cluster/elb", @mu_name, tagme_elb, credentials: @config['credentials'])
|
|
186
|
-
|
|
187
|
-
if @config['flavor'] == "Fargate"
|
|
188
|
-
fargate_subnets = []
|
|
189
|
-
@config["vpc"]["subnets"].each { |subnet|
|
|
190
|
-
subnet_obj = @vpc.getSubnet(cloud_id: subnet["subnet_id"].to_s, name: subnet["subnet_name"].to_s)
|
|
191
|
-
raise MuError, "Couldn't find a live subnet matching #{subnet} in #{@vpc} (#{@vpc.subnets})" if subnet_obj.nil?
|
|
192
|
-
next if !subnet_obj.private?
|
|
193
|
-
fargate_subnets << subnet_obj.cloud_id
|
|
194
|
-
}
|
|
195
|
-
podrole_arn = @deploy.findLitterMate(name: @config['name']+"pods", type: "roles").arn
|
|
196
|
-
poolnum = 0
|
|
197
|
-
poolthreads =[]
|
|
198
|
-
@config['kubernetes_pools'].each { |selectors|
|
|
199
|
-
profname = @mu_name+"-"+poolnum.to_s
|
|
200
|
-
poolnum += 1
|
|
201
|
-
desc = {
|
|
202
|
-
:fargate_profile_name => profname,
|
|
203
|
-
:cluster_name => @mu_name,
|
|
204
|
-
:pod_execution_role_arn => podrole_arn,
|
|
205
|
-
:selectors => selectors,
|
|
206
|
-
:subnets => fargate_subnets.sort,
|
|
207
|
-
:tags => @tags
|
|
208
|
-
}
|
|
209
|
-
begin
|
|
210
|
-
resp = MU::Cloud::AWS.eks(region: @config['region'], credentials: @config['credentials']).describe_fargate_profile(
|
|
211
|
-
cluster_name: @mu_name,
|
|
212
|
-
fargate_profile_name: profname
|
|
213
|
-
)
|
|
214
|
-
if resp and resp.fargate_profile
|
|
215
|
-
old_desc = MU.structToHash(resp.fargate_profile, stringify_keys: true)
|
|
216
|
-
new_desc = MU.structToHash(desc, stringify_keys: true)
|
|
217
|
-
["created_at", "status", "fargate_profile_arn"].each { |k|
|
|
218
|
-
old_desc.delete(k)
|
|
219
|
-
}
|
|
220
|
-
old_desc["subnets"].sort!
|
|
221
|
-
if !old_desc.eql?(new_desc)
|
|
222
|
-
MU.log "Deleting Fargate profile #{profname} in order to apply changes", MU::WARN, details: desc
|
|
223
|
-
MU::Cloud::AWS::ContainerCluster.purge_fargate_profile(profname, @mu_name, @config['region'], @credentials)
|
|
224
|
-
else
|
|
225
|
-
next
|
|
226
|
-
end
|
|
227
|
-
end
|
|
228
|
-
rescue Aws::EKS::Errors::ResourceNotFoundException
|
|
229
|
-
# This is just fine!
|
|
230
|
-
end
|
|
231
|
-
MU.log "Creating EKS Fargate profile #{profname}", details: desc
|
|
232
|
-
resp = MU::Cloud::AWS.eks(region: @config['region'], credentials: @config['credentials']).create_fargate_profile(desc)
|
|
233
|
-
begin
|
|
234
|
-
resp = MU::Cloud::AWS.eks(region: @config['region'], credentials: @config['credentials']).describe_fargate_profile(
|
|
235
|
-
cluster_name: @mu_name,
|
|
236
|
-
fargate_profile_name: profname
|
|
237
|
-
)
|
|
238
|
-
sleep 1 if resp.fargate_profile.status == "CREATING"
|
|
239
|
-
end while resp.fargate_profile.status == "CREATING"
|
|
240
|
-
MU.log "Creation of EKS Fargate profile #{profname} complete"
|
|
241
|
-
}
|
|
242
|
-
end
|
|
243
|
-
|
|
244
|
-
me = cloud_desc
|
|
245
|
-
@endpoint = me.endpoint
|
|
246
|
-
@cacert = me.certificate_authority.data
|
|
247
|
-
@cluster = @mu_name
|
|
248
|
-
if @config['flavor'] != "Fargate"
|
|
249
|
-
resp = MU::Cloud::AWS.iam(credentials: @config['credentials']).get_role(role_name: @mu_name+"WORKERS")
|
|
250
|
-
@worker_role_arn = resp.role.arn
|
|
251
|
-
end
|
|
252
|
-
kube_conf = @deploy.deploy_dir+"/kubeconfig-#{@config['name']}"
|
|
253
|
-
gitlab_helper = @deploy.deploy_dir+"/gitlab-eks-helper-#{@config['name']}.sh"
|
|
254
|
-
|
|
255
|
-
File.open(kube_conf, "w"){ |k|
|
|
256
|
-
k.puts kube.result(binding)
|
|
257
|
-
}
|
|
258
|
-
gitlab = ERB.new(File.read(MU.myRoot+"/extras/gitlab-eks-helper.sh.erb"))
|
|
259
|
-
File.open(gitlab_helper, "w"){ |k|
|
|
260
|
-
k.puts gitlab.result(binding)
|
|
261
|
-
}
|
|
127
|
+
apply_kubernetes_tags
|
|
128
|
+
create_fargate_kubernetes_profile if @config['flavor'] == "Fargate"
|
|
129
|
+
apply_kubernetes_resources
|
|
262
130
|
|
|
263
|
-
if @config['flavor'] != "Fargate"
|
|
264
|
-
eks_auth = @deploy.deploy_dir+"/eks-auth-cm-#{@config['name']}.yaml"
|
|
265
|
-
File.open(eks_auth, "w"){ |k|
|
|
266
|
-
k.puts configmap.result(binding)
|
|
267
|
-
}
|
|
268
|
-
authmap_cmd = %Q{#{MU::Master.kubectl} --kubeconfig "#{kube_conf}" apply -f "#{eks_auth}"}
|
|
269
|
-
MU.log "Configuring Kubernetes <=> IAM mapping for worker nodes", MU::NOTICE, details: authmap_cmd
|
|
270
|
-
# maybe guard this mess
|
|
271
|
-
%x{#{authmap_cmd}}
|
|
272
|
-
end
|
|
273
|
-
|
|
274
|
-
# and this one
|
|
275
|
-
admin_user_cmd = %Q{#{MU::Master.kubectl} --kubeconfig "#{kube_conf}" apply -f "#{MU.myRoot}/extras/admin-user.yaml"}
|
|
276
|
-
admin_role_cmd = %Q{#{MU::Master.kubectl} --kubeconfig "#{kube_conf}" apply -f "#{MU.myRoot}/extras/admin-role-binding.yaml"}
|
|
277
|
-
MU.log "Configuring Kubernetes admin-user and role", MU::NOTICE, details: admin_user_cmd+"\n"+admin_role_cmd
|
|
278
|
-
%x{#{admin_user_cmd}}
|
|
279
|
-
%x{#{admin_role_cmd}}
|
|
280
|
-
|
|
281
|
-
if @config['kubernetes_resources']
|
|
282
|
-
MU::Master.applyKubernetesResources(
|
|
283
|
-
@config['name'],
|
|
284
|
-
@config['kubernetes_resources'],
|
|
285
|
-
kubeconfig: kube_conf,
|
|
286
|
-
outputdir: @deploy.deploy_dir
|
|
287
|
-
)
|
|
288
|
-
end
|
|
289
|
-
|
|
290
|
-
MU.log %Q{How to interact with your EKS cluster\nkubectl --kubeconfig "#{kube_conf}" get all\nkubectl --kubeconfig "#{kube_conf}" create -f some_k8s_deploy.yml\nkubectl --kubeconfig "#{kube_conf}" get nodes}, MU::SUMMARY
|
|
291
131
|
elsif @config['flavor'] != "Fargate"
|
|
292
|
-
|
|
293
|
-
cluster: @mu_name
|
|
294
|
-
})
|
|
295
|
-
existing = {}
|
|
296
|
-
if resp
|
|
297
|
-
uuids = []
|
|
298
|
-
resp.container_instance_arns.each { |arn|
|
|
299
|
-
uuids << arn.sub(/^.*?:container-instance\//, "")
|
|
300
|
-
}
|
|
301
|
-
if uuids.size > 0
|
|
302
|
-
resp = MU::Cloud::AWS.ecs(region: @config['region'], credentials: @config['credentials']).describe_container_instances({
|
|
303
|
-
cluster: @mu_name,
|
|
304
|
-
container_instances: uuids
|
|
305
|
-
})
|
|
306
|
-
resp.container_instances.each { |i|
|
|
307
|
-
existing[i.ec2_instance_id] = i
|
|
308
|
-
}
|
|
309
|
-
end
|
|
310
|
-
end
|
|
311
|
-
|
|
312
|
-
serverpool.listNodes.each { |node|
|
|
313
|
-
resources = resource_lookup[node.cloud_desc.instance_type]
|
|
314
|
-
t = Thread.new {
|
|
315
|
-
ident_doc = nil
|
|
316
|
-
ident_doc_sig = nil
|
|
317
|
-
if !node.windows?
|
|
318
|
-
session = node.getSSHSession(10, 30)
|
|
319
|
-
ident_doc = session.exec!("curl -s http://169.254.169.254/latest/dynamic/instance-identity/document/")
|
|
320
|
-
ident_doc_sig = session.exec!("curl -s http://169.254.169.254/latest/dynamic/instance-identity/signature/")
|
|
321
|
-
else
|
|
322
|
-
begin
|
|
323
|
-
session = node.getWinRMSession(1, 60)
|
|
324
|
-
rescue Exception # XXX
|
|
325
|
-
session = node.getSSHSession(1, 60)
|
|
326
|
-
end
|
|
327
|
-
end
|
|
328
|
-
MU.log "Identity document for #{node}", MU::DEBUG, details: ident_doc
|
|
329
|
-
MU.log "Identity document signature for #{node}", MU::DEBUG, details: ident_doc_sig
|
|
330
|
-
params = {
|
|
331
|
-
:cluster => @mu_name,
|
|
332
|
-
:instance_identity_document => ident_doc,
|
|
333
|
-
:instance_identity_document_signature => ident_doc_sig,
|
|
334
|
-
:total_resources => [
|
|
335
|
-
{
|
|
336
|
-
:name => "CPU",
|
|
337
|
-
:type => "INTEGER",
|
|
338
|
-
:integer_value => resources["vcpu"].to_i
|
|
339
|
-
},
|
|
340
|
-
{
|
|
341
|
-
:name => "MEMORY",
|
|
342
|
-
:type => "INTEGER",
|
|
343
|
-
:integer_value => (resources["memory"]*1024*1024).to_i
|
|
344
|
-
}
|
|
345
|
-
]
|
|
346
|
-
}
|
|
347
|
-
if !existing.has_key?(node.cloud_id)
|
|
348
|
-
MU.log "Registering ECS instance #{node} in cluster #{@mu_name}", details: params
|
|
349
|
-
else
|
|
350
|
-
params[:container_instance_arn] = existing[node.cloud_id].container_instance_arn
|
|
351
|
-
MU.log "Updating ECS instance #{node} in cluster #{@mu_name}", MU::NOTICE, details: params
|
|
352
|
-
end
|
|
353
|
-
MU::Cloud::AWS.ecs(region: @config['region'], credentials: @config['credentials']).register_container_instance(params)
|
|
354
|
-
|
|
355
|
-
}
|
|
356
|
-
}
|
|
132
|
+
manage_ecs_workers
|
|
357
133
|
end
|
|
358
134
|
|
|
135
|
+
# ECS: manage containers/services/tasks
|
|
359
136
|
if @config['flavor'] != "EKS" and @config['containers']
|
|
360
137
|
|
|
361
|
-
security_groups = []
|
|
362
|
-
if @dependencies.has_key?("firewall_rule")
|
|
363
|
-
@dependencies['firewall_rule'].values.each { |sg|
|
|
364
|
-
security_groups << sg.cloud_id
|
|
365
|
-
}
|
|
366
|
-
end
|
|
367
|
-
|
|
368
|
-
tasks_registered = 0
|
|
369
|
-
retries = 0
|
|
370
|
-
svc_resp = begin
|
|
371
|
-
MU::Cloud::AWS.ecs(region: @config['region'], credentials: @config['credentials']).list_services(
|
|
372
|
-
cluster: arn
|
|
373
|
-
)
|
|
374
|
-
rescue Aws::ECS::Errors::ClusterNotFoundException => e
|
|
375
|
-
if retries < 10
|
|
376
|
-
sleep 5
|
|
377
|
-
retries += 1
|
|
378
|
-
retry
|
|
379
|
-
else
|
|
380
|
-
raise e
|
|
381
|
-
end
|
|
382
|
-
end
|
|
383
|
-
existing_svcs = svc_resp.service_arns.map { |s|
|
|
384
|
-
s.gsub(/.*?:service\/(.*)/, '\1')
|
|
385
|
-
}
|
|
386
|
-
|
|
387
138
|
# Reorganize things so that we have services and task definitions
|
|
388
139
|
# mapped to the set of containers they must contain
|
|
389
140
|
tasks = {}
|
|
390
|
-
created_generic_loggroup = false
|
|
391
141
|
|
|
392
142
|
@config['containers'].each { |c|
|
|
393
143
|
service_name = c['service'] ? @mu_name+"-"+c['service'].upcase : @mu_name
|
|
@@ -395,238 +145,35 @@ module MU
|
|
|
395
145
|
tasks[service_name] << c
|
|
396
146
|
}
|
|
397
147
|
|
|
148
|
+
existing_svcs = list_ecs_services
|
|
149
|
+
|
|
398
150
|
tasks.each_pair { |service_name, containers|
|
|
399
|
-
launch_type = @config['flavor'] == "ECS" ? "EC2" : "FARGATE"
|
|
400
|
-
cpu_total = 0
|
|
401
|
-
mem_total = 0
|
|
402
151
|
role_arn = nil
|
|
403
|
-
lbs = []
|
|
404
152
|
|
|
405
|
-
container_definitions = containers
|
|
406
|
-
|
|
153
|
+
container_definitions, role, lbs = get_ecs_container_definitions(containers)
|
|
154
|
+
role_arn ||= role
|
|
155
|
+
|
|
156
|
+
cpu_total = mem_total = 0
|
|
157
|
+
containers.each { |c|
|
|
407
158
|
cpu_total += c['cpu']
|
|
408
159
|
mem_total += c['memory']
|
|
409
|
-
|
|
410
|
-
if c["role"] and !role_arn
|
|
411
|
-
found = MU::MommaCat.findStray(
|
|
412
|
-
@config['cloud'],
|
|
413
|
-
"role",
|
|
414
|
-
cloud_id: c["role"]["id"],
|
|
415
|
-
name: c["role"]["name"],
|
|
416
|
-
deploy_id: c["role"]["deploy_id"] || @deploy.deploy_id,
|
|
417
|
-
dummy_ok: false
|
|
418
|
-
)
|
|
419
|
-
if found
|
|
420
|
-
found = found.first
|
|
421
|
-
if found and found.cloudobj
|
|
422
|
-
role_arn = found.cloudobj.arn
|
|
423
|
-
end
|
|
424
|
-
else
|
|
425
|
-
raise MuError, "Unable to find execution role from #{c["role"]}"
|
|
426
|
-
end
|
|
427
|
-
end
|
|
428
|
-
|
|
429
|
-
if c['loadbalancers'] != []
|
|
430
|
-
c['loadbalancers'].each {|lb|
|
|
431
|
-
found = @deploy.findLitterMate(name: lb['name'], type: "loadbalancer")
|
|
432
|
-
if found
|
|
433
|
-
MU.log "Mapping LB #{found.mu_name} to service #{c['name']}", MU::INFO
|
|
434
|
-
if found.cloud_desc.type != "classic"
|
|
435
|
-
elb_groups = MU::Cloud::AWS.elb2(region: @config['region'], credentials: @config['credentials']).describe_target_groups({
|
|
436
|
-
load_balancer_arn: found.cloud_desc.load_balancer_arn
|
|
437
|
-
})
|
|
438
|
-
matching_target_groups = []
|
|
439
|
-
elb_groups.target_groups.each { |tg|
|
|
440
|
-
if tg.port.to_i == lb['container_port'].to_i
|
|
441
|
-
matching_target_groups << {
|
|
442
|
-
arn: tg['target_group_arn'],
|
|
443
|
-
name: tg['target_group_name']
|
|
444
|
-
}
|
|
445
|
-
end
|
|
446
|
-
}
|
|
447
|
-
if matching_target_groups.length >= 1
|
|
448
|
-
MU.log "#{matching_target_groups.length} matching target groups found. Mapping #{container_name} to target group #{matching_target_groups.first['name']}", MU::INFO
|
|
449
|
-
lbs << {
|
|
450
|
-
container_name: container_name,
|
|
451
|
-
container_port: lb['container_port'],
|
|
452
|
-
target_group_arn: matching_target_groups.first[:arn]
|
|
453
|
-
}
|
|
454
|
-
else
|
|
455
|
-
raise MuError, "No matching target groups found"
|
|
456
|
-
end
|
|
457
|
-
elsif @config['flavor'] == "Fargate" && found.cloud_desc.type == "classic"
|
|
458
|
-
raise MuError, "Classic Load Balancers are not supported with Fargate."
|
|
459
|
-
else
|
|
460
|
-
MU.log "Mapping Classic LB #{found.mu_name} to service #{container_name}", MU::INFO
|
|
461
|
-
lbs << {
|
|
462
|
-
container_name: container_name,
|
|
463
|
-
container_port: lb['container_port'],
|
|
464
|
-
load_balancer_name: found.mu_name
|
|
465
|
-
}
|
|
466
|
-
end
|
|
467
|
-
else
|
|
468
|
-
raise MuError, "Unable to find loadbalancers from #{c["loadbalancers"].first['name']}"
|
|
469
|
-
end
|
|
470
|
-
}
|
|
471
|
-
end
|
|
472
|
-
|
|
473
|
-
params = {
|
|
474
|
-
name: @mu_name+"-"+c['name'].upcase,
|
|
475
|
-
image: c['image'],
|
|
476
|
-
memory: c['memory'],
|
|
477
|
-
cpu: c['cpu']
|
|
478
|
-
}
|
|
479
|
-
if !@config['vpc']
|
|
480
|
-
c['hostname'] ||= @mu_name+"-"+c['name'].upcase
|
|
481
|
-
end
|
|
482
|
-
[:essential, :hostname, :start_timeout, :stop_timeout, :user, :working_directory, :disable_networking, :privileged, :readonly_root_filesystem, :interactive, :pseudo_terminal, :links, :entry_point, :command, :dns_servers, :dns_search_domains, :docker_security_options, :port_mappings, :repository_credentials, :mount_points, :environment, :volumes_from, :secrets, :depends_on, :extra_hosts, :docker_labels, :ulimits, :system_controls, :health_check, :resource_requirements].each { |param|
|
|
483
|
-
if c.has_key?(param.to_s)
|
|
484
|
-
params[param] = if !c[param.to_s].nil? and (c[param.to_s].is_a?(Hash) or c[param.to_s].is_a?(Array))
|
|
485
|
-
MU.strToSym(c[param.to_s])
|
|
486
|
-
else
|
|
487
|
-
c[param.to_s]
|
|
488
|
-
end
|
|
489
|
-
end
|
|
490
|
-
}
|
|
491
|
-
if @config['vpc']
|
|
492
|
-
[:hostname, :dns_servers, :dns_search_domains, :links].each { |param|
|
|
493
|
-
if params[param]
|
|
494
|
-
MU.log "Container parameter #{param.to_s} not supported in VPC clusters, ignoring", MU::WARN
|
|
495
|
-
params.delete(param)
|
|
496
|
-
end
|
|
497
|
-
}
|
|
498
|
-
end
|
|
499
|
-
if @config['flavor'] == "Fargate"
|
|
500
|
-
[:privileged, :docker_security_options].each { |param|
|
|
501
|
-
if params[param]
|
|
502
|
-
MU.log "Container parameter #{param.to_s} not supported in Fargate clusters, ignoring", MU::WARN
|
|
503
|
-
params.delete(param)
|
|
504
|
-
end
|
|
505
|
-
}
|
|
506
|
-
end
|
|
507
|
-
if c['log_configuration']
|
|
508
|
-
log_obj = @deploy.findLitterMate(name: c['log_configuration']['options']['awslogs-group'], type: "logs")
|
|
509
|
-
if log_obj
|
|
510
|
-
c['log_configuration']['options']['awslogs-group'] = log_obj.mu_name
|
|
511
|
-
end
|
|
512
|
-
params[:log_configuration] = MU.strToSym(c['log_configuration'])
|
|
513
|
-
end
|
|
514
|
-
params
|
|
515
160
|
}
|
|
516
|
-
|
|
517
161
|
cpu_total = 2 if cpu_total == 0
|
|
518
162
|
mem_total = 2 if mem_total == 0
|
|
519
163
|
|
|
520
|
-
|
|
521
|
-
family: @deploy.deploy_id,
|
|
522
|
-
container_definitions: container_definitions,
|
|
523
|
-
requires_compatibilities: [launch_type]
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
if @config['volumes']
|
|
527
|
-
task_params[:volumes] = []
|
|
528
|
-
@config['volumes'].each { |v|
|
|
529
|
-
vol = { :name => v['name'] }
|
|
530
|
-
if v['type'] == "host"
|
|
531
|
-
vol[:host] = {}
|
|
532
|
-
if v['host_volume_source_path']
|
|
533
|
-
vol[:host][:source_path] = v['host_volume_source_path']
|
|
534
|
-
end
|
|
535
|
-
elsif v['type'] == "docker"
|
|
536
|
-
vol[:docker_volume_configuration] = MU.strToSym(v['docker_volume_configuration'])
|
|
537
|
-
else
|
|
538
|
-
raise MuError, "Invalid volume type '#{v['type']}' specified in ContainerCluster '#{@mu_name}'"
|
|
539
|
-
end
|
|
540
|
-
task_params[:volumes] << vol
|
|
541
|
-
}
|
|
542
|
-
end
|
|
543
|
-
|
|
544
|
-
if role_arn
|
|
545
|
-
task_params[:execution_role_arn] = role_arn
|
|
546
|
-
task_params[:task_role_arn] = role_arn
|
|
547
|
-
end
|
|
548
|
-
if @config['flavor'] == "Fargate"
|
|
549
|
-
task_params[:network_mode] = "awsvpc"
|
|
550
|
-
task_params[:cpu] = cpu_total.to_i.to_s
|
|
551
|
-
task_params[:memory] = mem_total.to_i.to_s
|
|
552
|
-
end
|
|
164
|
+
task_def = register_ecs_task(container_definitions, service_name, cpu_total, mem_total, role_arn: role_arn)
|
|
553
165
|
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
# XXX this helpfully keeps revisions, but let's compare anyway and avoid cluttering with identical ones
|
|
558
|
-
resp = MU::Cloud::AWS.ecs(region: @config['region'], credentials: @config['credentials']).register_task_definition(task_params)
|
|
166
|
+
create_update_ecs_service(task_def, service_name, lbs, existing_svcs)
|
|
167
|
+
existing_svcs << service_name
|
|
168
|
+
}
|
|
559
169
|
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
:
|
|
564
|
-
:service_name => service_name,
|
|
565
|
-
:launch_type => launch_type,
|
|
566
|
-
:task_definition => task_def,
|
|
567
|
-
:load_balancers => lbs
|
|
170
|
+
if tasks.size > 0
|
|
171
|
+
tasks_failing = false
|
|
172
|
+
MU.retrier(wait: 15, max: 10, loop_if: Proc.new { tasks_failing }){ |retries, _wait|
|
|
173
|
+
tasks_failing = !MU::Cloud::AWS::ContainerCluster.tasksRunning?(@mu_name, log: (retries > 0), region: @config['region'], credentials: @config['credentials'])
|
|
568
174
|
}
|
|
569
|
-
if @config['vpc']
|
|
570
|
-
subnet_ids = []
|
|
571
|
-
all_public = true
|
|
572
|
-
|
|
573
|
-
subnets =
|
|
574
|
-
if @config["vpc"]["subnets"].empty?
|
|
575
|
-
@vpc.subnets
|
|
576
|
-
else
|
|
577
|
-
subnet_objects= []
|
|
578
|
-
@config["vpc"]["subnets"].each { |subnet|
|
|
579
|
-
sobj = @vpc.getSubnet(cloud_id: subnet["subnet_id"], name: subnet["subnet_name"])
|
|
580
|
-
if sobj.nil?
|
|
581
|
-
MU.log "Got nil result from @vpc.getSubnet(cloud_id: #{subnet["subnet_id"]}, name: #{subnet["subnet_name"]})", MU::WARN
|
|
582
|
-
else
|
|
583
|
-
subnet_objects << sobj
|
|
584
|
-
end
|
|
585
|
-
}
|
|
586
|
-
subnet_objects
|
|
587
|
-
end
|
|
588
|
-
|
|
589
|
-
subnets.each { |subnet_obj|
|
|
590
|
-
subnet_ids << subnet_obj.cloud_id
|
|
591
|
-
all_public = false if subnet_obj.private?
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
service_params[:network_configuration] = {
|
|
595
|
-
:awsvpc_configuration => {
|
|
596
|
-
:subnets => subnet_ids,
|
|
597
|
-
:security_groups => security_groups,
|
|
598
|
-
:assign_public_ip => all_public ? "ENABLED" : "DISABLED"
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
end
|
|
602
175
|
|
|
603
|
-
if
|
|
604
|
-
MU.log "Creating Service #{service_name}"
|
|
605
|
-
|
|
606
|
-
resp = MU::Cloud::AWS.ecs(region: @config['region'], credentials: @config['credentials']).create_service(service_params)
|
|
607
|
-
else
|
|
608
|
-
service_params[:service] = service_params[:service_name].dup
|
|
609
|
-
service_params.delete(:service_name)
|
|
610
|
-
service_params.delete(:launch_type)
|
|
611
|
-
MU.log "Updating Service #{service_name}", MU::NOTICE, details: service_params
|
|
612
|
-
|
|
613
|
-
resp = MU::Cloud::AWS.ecs(region: @config['region'], credentials: @config['credentials']).update_service(service_params)
|
|
614
|
-
end
|
|
615
|
-
existing_svcs << service_name
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
max_retries = 10
|
|
619
|
-
retries = 0
|
|
620
|
-
if tasks_registered > 0
|
|
621
|
-
retry_me = false
|
|
622
|
-
begin
|
|
623
|
-
retry_me = !MU::Cloud::AWS::ContainerCluster.tasksRunning?(@mu_name, log: (retries > 0), region: @config['region'], credentials: @config['credentials'])
|
|
624
|
-
retries += 1
|
|
625
|
-
sleep 15 if retry_me
|
|
626
|
-
end while retry_me and retries < max_retries
|
|
627
|
-
tasks = nil
|
|
628
|
-
|
|
629
|
-
if retry_me
|
|
176
|
+
if tasks_failing
|
|
630
177
|
MU.log "Not all tasks successfully launched in cluster #{@mu_name}", MU::WARN
|
|
631
178
|
end
|
|
632
179
|
end
|
|
@@ -653,7 +200,7 @@ module MU
|
|
|
653
200
|
listme = services.slice!(0, (services.length >= 10 ? 10 : services.length))
|
|
654
201
|
if services.size > 0
|
|
655
202
|
tasks_defined.concat(
|
|
656
|
-
|
|
203
|
+
MU::Cloud::AWS.ecs(region: region, credentials: credentials).describe_services(
|
|
657
204
|
cluster: cluster,
|
|
658
205
|
services: listme
|
|
659
206
|
).services.map { |s| s.task_definition }
|
|
@@ -693,7 +240,6 @@ module MU
|
|
|
693
240
|
cluster: cluster,
|
|
694
241
|
tasks: task_ids
|
|
695
242
|
).tasks.each { |t|
|
|
696
|
-
task_name = t.task_definition_arn.sub(/^.*?:task-definition\/([^\/:]+)$/, '\1')
|
|
697
243
|
t.containers.each { |c|
|
|
698
244
|
containers[c.name] ||= {}
|
|
699
245
|
containers[c.name][t.desired_status] ||= {
|
|
@@ -736,10 +282,13 @@ MU.log c.name, MU::NOTICE, details: t
|
|
|
736
282
|
to_return
|
|
737
283
|
end
|
|
738
284
|
|
|
285
|
+
@cloud_desc_cache = nil
|
|
739
286
|
# Return the cloud layer descriptor for this EKS/ECS/Fargate cluster
|
|
740
287
|
# @return [OpenStruct]
|
|
741
|
-
def cloud_desc
|
|
742
|
-
if @
|
|
288
|
+
def cloud_desc(use_cache: true)
|
|
289
|
+
return @cloud_desc_cache if @cloud_desc_cache and use_cache
|
|
290
|
+
return nil if !@cloud_id
|
|
291
|
+
@cloud_desc_cache = if @config['flavor'] == "EKS" or
|
|
743
292
|
(@config['flavor'] == "Fargate" and !@config['containers'])
|
|
744
293
|
resp = MU::Cloud::AWS.eks(region: @config['region'], credentials: @config['credentials']).describe_cluster(
|
|
745
294
|
name: @cloud_id
|
|
@@ -751,6 +300,7 @@ MU.log c.name, MU::NOTICE, details: t
|
|
|
751
300
|
)
|
|
752
301
|
resp.clusters.first
|
|
753
302
|
end
|
|
303
|
+
@cloud_desc_cache
|
|
754
304
|
end
|
|
755
305
|
|
|
756
306
|
# Canonical Amazon Resource Number for this resource
|
|
@@ -777,7 +327,7 @@ MU.log c.name, MU::NOTICE, details: t
|
|
|
777
327
|
end
|
|
778
328
|
|
|
779
329
|
@@eks_versions = {}
|
|
780
|
-
@@
|
|
330
|
+
@@eks_version_semaphores = {}
|
|
781
331
|
# Use the AWS SSM API to fetch the current version of the Amazon Linux
|
|
782
332
|
# ECS-optimized AMI, so we can use it as a default AMI for ECS deploys.
|
|
783
333
|
# @param flavor [String]: ECS or EKS
|
|
@@ -790,24 +340,22 @@ MU.log c.name, MU::NOTICE, details: t
|
|
|
790
340
|
names: ["/aws/service/#{flavor.downcase}/optimized-ami/amazon-linux/recommended"]
|
|
791
341
|
)
|
|
792
342
|
else
|
|
793
|
-
@@
|
|
343
|
+
@@eks_version_semaphores[region] ||= Mutex.new
|
|
344
|
+
|
|
345
|
+
@@eks_version_semaphores[region].synchronize {
|
|
794
346
|
if !@@eks_versions[region]
|
|
795
347
|
@@eks_versions[region] ||= []
|
|
796
348
|
versions = {}
|
|
797
|
-
resp =
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
)
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
versions[Regexp.last_match[1]] = true
|
|
808
|
-
}
|
|
809
|
-
next_token = resp.next_token
|
|
810
|
-
end while !next_token.nil?
|
|
349
|
+
resp = MU::Cloud::AWS.ssm(region: region).get_parameters_by_path(
|
|
350
|
+
path: "/aws/service/#{flavor.downcase}/optimized-ami",
|
|
351
|
+
recursive: true,
|
|
352
|
+
max_results: 10 # as high as it goes, ugh
|
|
353
|
+
)
|
|
354
|
+
|
|
355
|
+
resp.parameters.each { |p|
|
|
356
|
+
p.name.match(/\/aws\/service\/eks\/optimized-ami\/([^\/]+?)\//)
|
|
357
|
+
versions[Regexp.last_match[1]] = true
|
|
358
|
+
}
|
|
811
359
|
@@eks_versions[region] = versions.keys.sort { |a, b| MU.version_sort(a, b) }
|
|
812
360
|
end
|
|
813
361
|
}
|
|
@@ -827,15 +375,31 @@ MU.log c.name, MU::NOTICE, details: t
|
|
|
827
375
|
nil
|
|
828
376
|
end
|
|
829
377
|
|
|
378
|
+
@@supported_eks_region_cache = []
|
|
379
|
+
@@eks_region_semaphore = Mutex.new
|
|
380
|
+
|
|
830
381
|
# Return the list of regions where we know EKS is supported.
|
|
831
382
|
def self.EKSRegions(credentials = nil)
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
383
|
+
@@eks_region_semaphore.synchronize {
|
|
384
|
+
if @@supported_eks_region_cache and !@@supported_eks_region_cache.empty?
|
|
385
|
+
return @@supported_eks_region_cache
|
|
386
|
+
end
|
|
387
|
+
start = Time.now
|
|
388
|
+
# the SSM API is painfully slow for large result sets, so thread
|
|
389
|
+
# these and do them in parallel
|
|
390
|
+
@@supported_eks_region_cache = []
|
|
391
|
+
region_threads = []
|
|
392
|
+
MU::Cloud::AWS.listRegions(credentials: credentials).each { |region|
|
|
393
|
+
region_threads << Thread.new(region) { |r|
|
|
394
|
+
r_start = Time.now
|
|
395
|
+
ami = getStandardImage("EKS", r)
|
|
396
|
+
@@supported_eks_region_cache << r if ami
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
region_threads.each { |t| t.join }
|
|
837
400
|
|
|
838
|
-
|
|
401
|
+
@@supported_eks_region_cache
|
|
402
|
+
}
|
|
839
403
|
end
|
|
840
404
|
|
|
841
405
|
# Does this resource type exist as a global (cloud-wide) artifact, or
|
|
@@ -856,156 +420,143 @@ MU.log c.name, MU::NOTICE, details: t
|
|
|
856
420
|
# @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
|
|
857
421
|
# @param region [String]: The cloud provider region
|
|
858
422
|
# @return [void]
|
|
859
|
-
def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
if resp and resp.cluster_arns and resp.cluster_arns.size > 0
|
|
864
|
-
resp.cluster_arns.each { |arn|
|
|
865
|
-
if arn.match(/:cluster\/(#{MU.deploy_id}[^:]+)$/)
|
|
866
|
-
cluster = Regexp.last_match[1]
|
|
867
|
-
|
|
868
|
-
svc_resp = MU::Cloud::AWS.ecs(region: region, credentials: credentials).list_services(
|
|
869
|
-
cluster: arn
|
|
870
|
-
)
|
|
871
|
-
if svc_resp and svc_resp.service_arns
|
|
872
|
-
svc_resp.service_arns.each { |svc_arn|
|
|
873
|
-
svc_name = svc_arn.gsub(/.*?:service\/(.*)/, '\1')
|
|
874
|
-
MU.log "Deleting Service #{svc_name} from ECS Cluster #{cluster}"
|
|
875
|
-
if !noop
|
|
876
|
-
MU::Cloud::AWS.ecs(region: region, credentials: credentials).delete_service(
|
|
877
|
-
cluster: arn,
|
|
878
|
-
service: svc_name,
|
|
879
|
-
force: true # man forget scaling up and down if we're just deleting the cluster
|
|
880
|
-
)
|
|
881
|
-
end
|
|
882
|
-
}
|
|
883
|
-
end
|
|
423
|
+
def self.cleanup(noop: false, deploy_id: MU.deploy_id, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
|
|
424
|
+
MU.log "AWS::ContainerCluster.cleanup: need to support flags['known']", MU::DEBUG, details: flags
|
|
425
|
+
MU.log "Placeholder: AWS ContainerCluster artifacts do not support tags, so ignoremaster cleanup flag has no effect", MU::DEBUG, details: ignoremaster
|
|
884
426
|
|
|
885
|
-
|
|
886
|
-
cluster: cluster
|
|
887
|
-
})
|
|
888
|
-
if instances
|
|
889
|
-
instances.container_instance_arns.each { |instance_arn|
|
|
890
|
-
uuid = instance_arn.sub(/^.*?:container-instance\//, "")
|
|
891
|
-
MU.log "Deregistering instance #{uuid} from ECS Cluster #{cluster}"
|
|
892
|
-
if !noop
|
|
893
|
-
resp = MU::Cloud::AWS.ecs(credentials: credentials, region: region).deregister_container_instance({
|
|
894
|
-
cluster: cluster,
|
|
895
|
-
container_instance: uuid,
|
|
896
|
-
force: true,
|
|
897
|
-
})
|
|
898
|
-
end
|
|
899
|
-
}
|
|
900
|
-
end
|
|
901
|
-
MU.log "Deleting ECS Cluster #{cluster}"
|
|
902
|
-
if !noop
|
|
903
|
-
# TODO de-register container instances
|
|
904
|
-
begin
|
|
905
|
-
deletion = MU::Cloud::AWS.ecs(credentials: credentials, region: region).delete_cluster(
|
|
906
|
-
cluster: cluster
|
|
907
|
-
)
|
|
908
|
-
rescue Aws::ECS::Errors::ClusterContainsTasksException => e
|
|
909
|
-
sleep 5
|
|
910
|
-
retry
|
|
911
|
-
end
|
|
912
|
-
end
|
|
913
|
-
end
|
|
914
|
-
}
|
|
915
|
-
end
|
|
427
|
+
purge_ecs_clusters(noop: noop, region: region, credentials: credentials, deploy_id: deploy_id)
|
|
916
428
|
|
|
917
|
-
|
|
918
|
-
family_prefix: MU.deploy_id
|
|
919
|
-
)
|
|
429
|
+
purge_eks_clusters(noop: noop, region: region, credentials: credentials, deploy_id: deploy_id)
|
|
920
430
|
|
|
921
|
-
|
|
922
|
-
tasks.task_definition_arns.each { |arn|
|
|
923
|
-
MU.log "Deregistering Fargate task definition #{arn}"
|
|
924
|
-
if !noop
|
|
925
|
-
MU::Cloud::AWS.ecs(region: region, credentials: credentials).deregister_task_definition(
|
|
926
|
-
task_definition: arn
|
|
927
|
-
)
|
|
928
|
-
end
|
|
929
|
-
}
|
|
930
|
-
end
|
|
931
|
-
|
|
932
|
-
return if !MU::Cloud::AWS::ContainerCluster.EKSRegions.include?(region)
|
|
431
|
+
end
|
|
933
432
|
|
|
433
|
+
def self.purge_eks_clusters(noop: false, region: MU.curRegion, credentials: nil, deploy_id: MU.deploy_id)
|
|
934
434
|
resp = begin
|
|
935
435
|
MU::Cloud::AWS.eks(credentials: credentials, region: region).list_clusters
|
|
936
436
|
rescue Aws::EKS::Errors::AccessDeniedException
|
|
937
437
|
# EKS isn't actually live in this region, even though SSM lists
|
|
938
438
|
# base images for it
|
|
439
|
+
if @@supported_eks_region_cache
|
|
440
|
+
@@supported_eks_region_cache.delete(region)
|
|
441
|
+
end
|
|
939
442
|
return
|
|
940
443
|
end
|
|
941
444
|
|
|
445
|
+
return if !resp or !resp.clusters
|
|
942
446
|
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
if cluster.match(/^#{MU.deploy_id}-/)
|
|
447
|
+
resp.clusters.each { |cluster|
|
|
448
|
+
if cluster.match(/^#{deploy_id}-/)
|
|
946
449
|
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
450
|
+
desc = MU::Cloud::AWS.eks(credentials: credentials, region: region).describe_cluster(
|
|
451
|
+
name: cluster
|
|
452
|
+
).cluster
|
|
950
453
|
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
454
|
+
profiles = MU::Cloud::AWS.eks(region: region, credentials: credentials).list_fargate_profiles(
|
|
455
|
+
cluster_name: cluster
|
|
456
|
+
)
|
|
457
|
+
if profiles and profiles.fargate_profile_names
|
|
458
|
+
profiles.fargate_profile_names.each { |profile|
|
|
459
|
+
MU.log "Deleting Fargate EKS profile #{profile}"
|
|
460
|
+
next if noop
|
|
461
|
+
MU::Cloud::AWS::ContainerCluster.purge_fargate_profile(profile, cluster, region, credentials)
|
|
462
|
+
}
|
|
463
|
+
end
|
|
464
|
+
|
|
465
|
+
remove_kubernetes_tags(cluster, desc, region: region, credentials: credentials, noop: noop)
|
|
961
466
|
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
if
|
|
978
|
-
MU
|
|
979
|
-
MU::Cloud::AWS.removeTag("kubernetes.io/cluster/elb", cluster, untag)
|
|
467
|
+
MU.log "Deleting EKS Cluster #{cluster}"
|
|
468
|
+
next if noop
|
|
469
|
+
MU::Cloud::AWS.eks(credentials: credentials, region: region).delete_cluster(
|
|
470
|
+
name: cluster
|
|
471
|
+
)
|
|
472
|
+
|
|
473
|
+
status = nil
|
|
474
|
+
loop_if = Proc.new {
|
|
475
|
+
status != "FAILED"
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
MU.retrier(ignoreme: [Aws::EKS::Errors::ResourceNotFoundException], wait: 60){ |retries, _wait|
|
|
479
|
+
status = MU::Cloud::AWS.eks(credentials: credentials, region: region).describe_cluster(
|
|
480
|
+
name: cluster
|
|
481
|
+
).cluster.status
|
|
482
|
+
if retries > 0 and (retries % 3) == 0
|
|
483
|
+
MU.log "Waiting for EKS cluster #{cluster} to finish deleting (status #{status})", MU::NOTICE
|
|
980
484
|
end
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
485
|
+
}
|
|
486
|
+
# MU::Cloud.resourceClass("AWS", "Server").removeIAMProfile(cluster)
|
|
487
|
+
end
|
|
488
|
+
}
|
|
489
|
+
end
|
|
490
|
+
private_class_method :purge_eks_clusters
|
|
491
|
+
|
|
492
|
+
def self.purge_ecs_clusters(noop: false, region: MU.curRegion, credentials: nil, deploy_id: MU.deploy_id)
|
|
493
|
+
start = Time.now
|
|
494
|
+
resp = MU::Cloud::AWS.ecs(credentials: credentials, region: region).list_clusters
|
|
495
|
+
|
|
496
|
+
return if !resp or !resp.cluster_arns or resp.cluster_arns.empty?
|
|
497
|
+
|
|
498
|
+
resp.cluster_arns.each { |arn|
|
|
499
|
+
if arn.match(/:cluster\/(#{deploy_id}[^:]+)$/)
|
|
500
|
+
cluster = Regexp.last_match[1]
|
|
501
|
+
|
|
502
|
+
svc_resp = MU::Cloud::AWS.ecs(region: region, credentials: credentials).list_services(
|
|
503
|
+
cluster: arn
|
|
504
|
+
)
|
|
505
|
+
if svc_resp and svc_resp.service_arns
|
|
506
|
+
svc_resp.service_arns.each { |svc_arn|
|
|
507
|
+
svc_name = svc_arn.gsub(/.*?:service\/(.*)/, '\1')
|
|
508
|
+
MU.log "Deleting Service #{svc_name} from ECS Cluster #{cluster}"
|
|
509
|
+
next if noop
|
|
510
|
+
MU::Cloud::AWS.ecs(region: region, credentials: credentials).delete_service(
|
|
511
|
+
cluster: arn,
|
|
512
|
+
service: svc_name,
|
|
513
|
+
force: true # man forget scaling up and down if we're just deleting the cluster
|
|
985
514
|
)
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
515
|
+
}
|
|
516
|
+
end
|
|
517
|
+
|
|
518
|
+
instances = MU::Cloud::AWS.ecs(credentials: credentials, region: region).list_container_instances({
|
|
519
|
+
cluster: cluster
|
|
520
|
+
})
|
|
521
|
+
if instances
|
|
522
|
+
instances.container_instance_arns.each { |instance_arn|
|
|
523
|
+
uuid = instance_arn.sub(/^.*?:container-instance\//, "")
|
|
524
|
+
MU.log "Deregistering instance #{uuid} from ECS Cluster #{cluster}"
|
|
525
|
+
next if noop
|
|
526
|
+
resp = MU::Cloud::AWS.ecs(credentials: credentials, region: region).deregister_container_instance({
|
|
527
|
+
cluster: cluster,
|
|
528
|
+
container_instance: uuid,
|
|
529
|
+
force: true,
|
|
530
|
+
})
|
|
531
|
+
}
|
|
532
|
+
end
|
|
533
|
+
MU.log "Deleting ECS Cluster #{cluster}"
|
|
534
|
+
next if noop
|
|
535
|
+
MU.retrier([Aws::ECS::Errors::ClusterContainsTasksException], wait: 5){
|
|
536
|
+
# TODO de-register container instances
|
|
537
|
+
MU::Cloud::AWS.ecs(credentials: credentials, region: region).delete_cluster(
|
|
538
|
+
cluster: cluster
|
|
539
|
+
)
|
|
540
|
+
}
|
|
541
|
+
end
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
tasks = MU::Cloud::AWS.ecs(region: region, credentials: credentials).list_task_definitions(
|
|
545
|
+
family_prefix: deploy_id
|
|
546
|
+
)
|
|
547
|
+
|
|
548
|
+
if tasks and tasks.task_definition_arns
|
|
549
|
+
tasks.task_definition_arns.each { |arn|
|
|
550
|
+
MU.log "Deregistering Fargate task definition #{arn}"
|
|
551
|
+
if !noop
|
|
552
|
+
MU::Cloud::AWS.ecs(region: region, credentials: credentials).deregister_task_definition(
|
|
553
|
+
task_definition: arn
|
|
554
|
+
)
|
|
1005
555
|
end
|
|
1006
556
|
}
|
|
1007
557
|
end
|
|
1008
558
|
end
|
|
559
|
+
private_class_method :purge_ecs_clusters
|
|
1009
560
|
|
|
1010
561
|
# Locate an existing container_cluster.
|
|
1011
562
|
# @return [Hash<String,OpenStruct>]: The cloud provider's complete descriptions of matching container_clusters.
|
|
@@ -1016,10 +567,8 @@ MU.log c.name, MU::NOTICE, details: t
|
|
|
1016
567
|
resp = MU::Cloud::AWS.ecs(region: args[:region], credentials: args[:credentials]).describe_clusters(clusters: [args[:cloud_id]])
|
|
1017
568
|
if resp.clusters and resp.clusters.size > 0
|
|
1018
569
|
found[args[:cloud_id]] = resp.clusters.first
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
# XXX name collision is possible here
|
|
1022
|
-
if found.size == 0
|
|
570
|
+
else
|
|
571
|
+
# XXX misses due to name collision are possible here
|
|
1023
572
|
desc = MU::Cloud::AWS.eks(region: args[:region], credentials: args[:credentials]).describe_cluster(name: args[:cloud_id])
|
|
1024
573
|
found[args[:cloud_id]] = desc.cluster if desc and desc.cluster
|
|
1025
574
|
end
|
|
@@ -1027,14 +576,14 @@ MU.log c.name, MU::NOTICE, details: t
|
|
|
1027
576
|
next_token = nil
|
|
1028
577
|
begin
|
|
1029
578
|
resp = MU::Cloud::AWS.ecs(region: args[:region], credentials: args[:credentials]).list_clusters(next_token: next_token)
|
|
1030
|
-
if resp
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
579
|
+
break if !resp or !resp.cluster_arns
|
|
580
|
+
next_token = resp.next_token
|
|
581
|
+
names = resp.cluster_arns.map { |a| a.sub(/.*?:cluster\//, '') }
|
|
582
|
+
descs = MU::Cloud::AWS.ecs(region: args[:region], credentials: args[:credentials]).describe_clusters(clusters: names)
|
|
583
|
+
if descs and descs.clusters
|
|
584
|
+
descs.clusters.each { |c|
|
|
585
|
+
found[c.cluster_name] = c
|
|
586
|
+
}
|
|
1038
587
|
end
|
|
1039
588
|
end while next_token
|
|
1040
589
|
|
|
@@ -1042,14 +591,12 @@ MU.log c.name, MU::NOTICE, details: t
|
|
|
1042
591
|
next_token = nil
|
|
1043
592
|
begin
|
|
1044
593
|
resp = MU::Cloud::AWS.eks(region: args[:region], credentials: args[:credentials]).list_clusters(next_token: next_token)
|
|
1045
|
-
if resp
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
next_token = resp.next_token
|
|
1052
|
-
end
|
|
594
|
+
break if !resp or !resp.clusters
|
|
595
|
+
resp.clusters.each { |c|
|
|
596
|
+
desc = MU::Cloud::AWS.eks(region: args[:region], credentials: args[:credentials]).describe_cluster(name: c)
|
|
597
|
+
found[c] = desc.cluster if desc and desc.cluster
|
|
598
|
+
}
|
|
599
|
+
next_token = resp.next_token
|
|
1053
600
|
rescue Aws::EKS::Errors::AccessDeniedException
|
|
1054
601
|
# not all regions support EKS
|
|
1055
602
|
end while next_token
|
|
@@ -1059,9 +606,9 @@ MU.log c.name, MU::NOTICE, details: t
|
|
|
1059
606
|
end
|
|
1060
607
|
|
|
1061
608
|
# Cloud-specific configuration properties.
|
|
1062
|
-
# @param
|
|
609
|
+
# @param _config [MU::Config]: The calling MU::Config object
|
|
1063
610
|
# @return [Array<Array,Hash>]: List of required fields, and json-schema Hash of cloud-specific configuration parameters for this resource
|
|
1064
|
-
def self.schema(
|
|
611
|
+
def self.schema(_config)
|
|
1065
612
|
toplevel_required = []
|
|
1066
613
|
|
|
1067
614
|
schema = {
|
|
@@ -1685,18 +1232,18 @@ MU.log c.name, MU::NOTICE, details: t
|
|
|
1685
1232
|
# @return [Boolean]: True if validation succeeded, False otherwise
|
|
1686
1233
|
def self.validateConfig(cluster, configurator)
|
|
1687
1234
|
ok = true
|
|
1688
|
-
|
|
1689
|
-
cluster['size'] = MU::Cloud
|
|
1235
|
+
start = Time.now
|
|
1236
|
+
cluster['size'] = MU::Cloud.resourceClass("AWS", "Server").validateInstanceType(cluster["instance_type"], cluster["region"])
|
|
1690
1237
|
ok = false if cluster['size'].nil?
|
|
1691
1238
|
|
|
1692
1239
|
cluster["flavor"] = "EKS" if cluster["flavor"].match(/^Kubernetes$/i)
|
|
1693
1240
|
|
|
1694
|
-
if cluster["flavor"] == "ECS" and cluster["kubernetes"] and !MU::Cloud::AWS.isGovCloud?(cluster["region"])
|
|
1241
|
+
if cluster["flavor"] == "ECS" and cluster["kubernetes"] and !MU::Cloud::AWS.isGovCloud?(cluster["region"]) and !cluster["containers"] and MU::Cloud::AWS::ContainerCluster.EKSRegions(cluster['credentials']).include?(cluster['region'])
|
|
1695
1242
|
cluster["flavor"] = "EKS"
|
|
1696
1243
|
MU.log "Setting flavor of ContainerCluster '#{cluster['name']}' to EKS ('kubernetes' stanza was specified)", MU::NOTICE
|
|
1697
1244
|
end
|
|
1698
1245
|
|
|
1699
|
-
if cluster["flavor"] == "EKS" and !MU::Cloud::AWS::ContainerCluster.EKSRegions.include?(cluster['region'])
|
|
1246
|
+
if cluster["flavor"] == "EKS" and !MU::Cloud::AWS::ContainerCluster.EKSRegions(cluster['credentials']).include?(cluster['region'])
|
|
1700
1247
|
MU.log "EKS is only available in some regions", MU::ERR, details: MU::Cloud::AWS::ContainerCluster.EKSRegions
|
|
1701
1248
|
ok = false
|
|
1702
1249
|
end
|
|
@@ -1766,7 +1313,7 @@ MU.log c.name, MU::NOTICE, details: t
|
|
|
1766
1313
|
end
|
|
1767
1314
|
|
|
1768
1315
|
if !created_generic_loggroup
|
|
1769
|
-
cluster
|
|
1316
|
+
MU::Config.addDependency(cluster, logname, "log")
|
|
1770
1317
|
logdesc = {
|
|
1771
1318
|
"name" => logname,
|
|
1772
1319
|
"region" => cluster["region"],
|
|
@@ -1805,10 +1352,7 @@ MU.log c.name, MU::NOTICE, details: t
|
|
|
1805
1352
|
}
|
|
1806
1353
|
configurator.insertKitten(roledesc, "roles")
|
|
1807
1354
|
|
|
1808
|
-
cluster
|
|
1809
|
-
"type" => "role",
|
|
1810
|
-
"name" => rolename
|
|
1811
|
-
}
|
|
1355
|
+
MU::Config.addDependency(cluster, rolename, "role")
|
|
1812
1356
|
end
|
|
1813
1357
|
|
|
1814
1358
|
created_generic_loggroup = true
|
|
@@ -1837,11 +1381,10 @@ MU.log c.name, MU::NOTICE, details: t
|
|
|
1837
1381
|
role["tags"] = cluster["tags"] if !cluster["tags"].nil?
|
|
1838
1382
|
role["optional_tags"] = cluster["optional_tags"] if !cluster["optional_tags"].nil?
|
|
1839
1383
|
configurator.insertKitten(role, "roles")
|
|
1840
|
-
cluster[
|
|
1841
|
-
|
|
1842
|
-
"
|
|
1843
|
-
|
|
1844
|
-
}
|
|
1384
|
+
MU::Config.addDependency(cluster, cluster["name"]+"pods", "role", phase: "groom")
|
|
1385
|
+
if !MU::Master.kubectl
|
|
1386
|
+
MU.log "Since I can't find a kubectl executable, you will have to handle all service account, user, and role bindings manually!", MU::WARN
|
|
1387
|
+
end
|
|
1845
1388
|
end
|
|
1846
1389
|
|
|
1847
1390
|
if MU::Cloud::AWS.isGovCloud?(cluster["region"]) and cluster["flavor"] == "EKS"
|
|
@@ -1851,7 +1394,8 @@ MU.log c.name, MU::NOTICE, details: t
|
|
|
1851
1394
|
|
|
1852
1395
|
|
|
1853
1396
|
if ["ECS", "EKS"].include?(cluster["flavor"])
|
|
1854
|
-
|
|
1397
|
+
version = cluster["kubernetes"] ? cluster['kubernetes']['version'] : nil
|
|
1398
|
+
std_ami = getStandardImage(cluster["flavor"], cluster['region'], version: version, gpu: cluster['gpu'])
|
|
1855
1399
|
cluster["host_image"] ||= std_ami
|
|
1856
1400
|
if cluster["host_image"] != std_ami
|
|
1857
1401
|
if cluster["flavor"] == "ECS"
|
|
@@ -1939,34 +1483,32 @@ MU.log c.name, MU::NOTICE, details: t
|
|
|
1939
1483
|
end
|
|
1940
1484
|
|
|
1941
1485
|
if cluster["flavor"] == "EKS"
|
|
1486
|
+
|
|
1487
|
+
if !MU::Master.kubectl
|
|
1488
|
+
MU.log "Without a kubectl executable, I cannot bind IAM roles to EKS worker nodes", MU::ERR
|
|
1489
|
+
ok = false
|
|
1490
|
+
end
|
|
1942
1491
|
worker_pool["canned_iam_policies"] = [
|
|
1943
1492
|
"AmazonEKSWorkerNodePolicy",
|
|
1944
1493
|
"AmazonEKS_CNI_Policy",
|
|
1945
1494
|
"AmazonEC2ContainerRegistryReadOnly"
|
|
1946
1495
|
]
|
|
1947
|
-
worker_pool["
|
|
1948
|
-
|
|
1949
|
-
"type" => "container_cluster",
|
|
1950
|
-
"name" => cluster['name']
|
|
1951
|
-
}
|
|
1952
|
-
]
|
|
1953
|
-
worker_pool["run_list"] = ["mu-tools::eks"]
|
|
1496
|
+
MU::Config.addDependency(worker_pool, cluster["name"], "container_cluster")
|
|
1497
|
+
worker_pool["run_list"] = ["recipe[mu-tools::eks]"]
|
|
1954
1498
|
worker_pool["run_list"].concat(cluster["run_list"]) if cluster["run_list"]
|
|
1955
1499
|
MU::Config::Server.common_properties.keys.each { |k|
|
|
1956
1500
|
if cluster[k] and !worker_pool[k]
|
|
1957
1501
|
worker_pool[k] = cluster[k]
|
|
1958
1502
|
end
|
|
1959
1503
|
}
|
|
1960
|
-
|
|
1504
|
+
else
|
|
1505
|
+
worker_pool["groom"] = false # don't meddle with ECS workers unnecessarily
|
|
1961
1506
|
end
|
|
1962
1507
|
|
|
1963
1508
|
configurator.insertKitten(worker_pool, "server_pools")
|
|
1964
1509
|
|
|
1965
1510
|
if cluster["flavor"] == "ECS"
|
|
1966
|
-
cluster["
|
|
1967
|
-
"name" => cluster["name"]+"workers",
|
|
1968
|
-
"type" => "server_pool",
|
|
1969
|
-
}
|
|
1511
|
+
MU::Config.addDependency(cluster, cluster["name"]+"workers", "server_pool")
|
|
1970
1512
|
end
|
|
1971
1513
|
|
|
1972
1514
|
end
|
|
@@ -1988,18 +1530,17 @@ MU.log c.name, MU::NOTICE, details: t
|
|
|
1988
1530
|
role["tags"] = cluster["tags"] if !cluster["tags"].nil?
|
|
1989
1531
|
role["optional_tags"] = cluster["optional_tags"] if !cluster["optional_tags"].nil?
|
|
1990
1532
|
configurator.insertKitten(role, "roles")
|
|
1991
|
-
cluster[
|
|
1992
|
-
"type" => "role",
|
|
1993
|
-
"name" => cluster["name"]+"controlplane",
|
|
1994
|
-
"phase" => "groom"
|
|
1995
|
-
}
|
|
1533
|
+
MU::Config.addDependency(cluster, cluster["name"]+"controlplane", "role", phase: "groom")
|
|
1996
1534
|
end
|
|
1997
1535
|
|
|
1998
1536
|
ok
|
|
1999
1537
|
end
|
|
2000
1538
|
|
|
2001
|
-
|
|
2002
|
-
|
|
1539
|
+
# Delete a Fargate profile, needed both for cleanup and regroom updates
|
|
1540
|
+
# @param profile [String]:
|
|
1541
|
+
# @param cluster [String]:
|
|
1542
|
+
# @param region [String]:
|
|
1543
|
+
# @param credentials [String]:
|
|
2003
1544
|
def self.purge_fargate_profile(profile, cluster, region, credentials)
|
|
2004
1545
|
check = begin
|
|
2005
1546
|
MU::Cloud::AWS.eks(region: region, credentials: credentials).delete_fargate_profile(
|
|
@@ -2012,28 +1553,467 @@ MU.log c.name, MU::NOTICE, details: t
|
|
|
2012
1553
|
sleep 10
|
|
2013
1554
|
retry
|
|
2014
1555
|
end
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
begin
|
|
2018
|
-
begin
|
|
1556
|
+
|
|
1557
|
+
loop_if = Proc.new {
|
|
2019
1558
|
check = MU::Cloud::AWS.eks(region: region, credentials: credentials).describe_fargate_profile(
|
|
2020
1559
|
cluster_name: cluster,
|
|
2021
1560
|
fargate_profile_name: profile
|
|
2022
1561
|
)
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
end
|
|
1562
|
+
check.fargate_profile.status == "DELETING"
|
|
1563
|
+
}
|
|
2026
1564
|
|
|
1565
|
+
MU.retrier(ignoreme: [Aws::EKS::Errors::ResourceNotFoundException], wait: 30, max: 40, loop_if: loop_if) {
|
|
2027
1566
|
if check.fargate_profile.status != "DELETING"
|
|
2028
|
-
MU.log "Failed to delete Fargate EKS profile #{profile}", MU::ERR, details: check
|
|
2029
1567
|
break
|
|
2030
|
-
|
|
2031
|
-
if retries > 0 and (retries % 3) == 0
|
|
1568
|
+
elsif retries > 0 and (retries % 3) == 0
|
|
2032
1569
|
MU.log "Waiting for Fargate EKS profile #{profile} to delete (status #{check.fargate_profile.status})", MU::NOTICE
|
|
2033
1570
|
end
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
1571
|
+
}
|
|
1572
|
+
end
|
|
1573
|
+
|
|
1574
|
+
private
|
|
1575
|
+
|
|
1576
|
+
def apply_kubernetes_resources
|
|
1577
|
+
kube = ERB.new(File.read(MU.myRoot+"/cookbooks/mu-tools/templates/default/kubeconfig-eks.erb"))
|
|
1578
|
+
configmap = ERB.new(File.read(MU.myRoot+"/extras/aws-auth-cm.yaml.erb"))
|
|
1579
|
+
@endpoint = cloud_desc.endpoint
|
|
1580
|
+
@cacert = cloud_desc.certificate_authority.data
|
|
1581
|
+
@cluster = @mu_name
|
|
1582
|
+
if @config['flavor'] != "Fargate"
|
|
1583
|
+
resp = MU::Cloud::AWS.iam(credentials: @config['credentials']).get_role(role_name: @mu_name+"WORKERS")
|
|
1584
|
+
@worker_role_arn = resp.role.arn
|
|
1585
|
+
end
|
|
1586
|
+
kube_conf = @deploy.deploy_dir+"/kubeconfig-#{@config['name']}"
|
|
1587
|
+
gitlab_helper = @deploy.deploy_dir+"/gitlab-eks-helper-#{@config['name']}.sh"
|
|
1588
|
+
|
|
1589
|
+
File.open(kube_conf, "w"){ |k|
|
|
1590
|
+
k.puts kube.result(binding)
|
|
1591
|
+
}
|
|
1592
|
+
gitlab = ERB.new(File.read(MU.myRoot+"/extras/gitlab-eks-helper.sh.erb"))
|
|
1593
|
+
File.open(gitlab_helper, "w"){ |k|
|
|
1594
|
+
k.puts gitlab.result(binding)
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
if @config['flavor'] != "Fargate"
|
|
1598
|
+
eks_auth = @deploy.deploy_dir+"/eks-auth-cm-#{@config['name']}.yaml"
|
|
1599
|
+
File.open(eks_auth, "w"){ |k|
|
|
1600
|
+
k.puts configmap.result(binding)
|
|
1601
|
+
}
|
|
1602
|
+
authmap_cmd = %Q{#{MU::Master.kubectl} --kubeconfig "#{kube_conf}" apply -f "#{eks_auth}"}
|
|
1603
|
+
|
|
1604
|
+
MU.log "Configuring Kubernetes <=> IAM mapping for worker nodes", MU::NOTICE, details: authmap_cmd
|
|
1605
|
+
|
|
1606
|
+
MU.retrier(max: 10, wait: 10, loop_if: Proc.new {$?.exitstatus != 0}){
|
|
1607
|
+
puts %x{#{authmap_cmd}}
|
|
1608
|
+
}
|
|
1609
|
+
raise MuError, "Failed to apply #{authmap_cmd}" if $?.exitstatus != 0
|
|
1610
|
+
end
|
|
1611
|
+
|
|
1612
|
+
if MU::Master.kubectl
|
|
1613
|
+
admin_user_cmd = %Q{#{MU::Master.kubectl} --kubeconfig "#{kube_conf}" apply -f "#{MU.myRoot}/extras/admin-user.yaml"}
|
|
1614
|
+
admin_role_cmd = %Q{#{MU::Master.kubectl} --kubeconfig "#{kube_conf}" apply -f "#{MU.myRoot}/extras/admin-role-binding.yaml"}
|
|
1615
|
+
MU.log "Configuring Kubernetes admin-user and role", MU::NOTICE, details: admin_user_cmd+"\n"+admin_role_cmd
|
|
1616
|
+
%x{#{admin_user_cmd}}
|
|
1617
|
+
%x{#{admin_role_cmd}}
|
|
1618
|
+
|
|
1619
|
+
if @config['kubernetes_resources']
|
|
1620
|
+
MU::Master.applyKubernetesResources(
|
|
1621
|
+
@config['name'],
|
|
1622
|
+
@config['kubernetes_resources'],
|
|
1623
|
+
kubeconfig: kube_conf,
|
|
1624
|
+
outputdir: @deploy.deploy_dir
|
|
1625
|
+
)
|
|
1626
|
+
end
|
|
1627
|
+
end
|
|
1628
|
+
|
|
1629
|
+
MU.log %Q{How to interact with your EKS cluster\nkubectl --kubeconfig "#{kube_conf}" get all\nkubectl --kubeconfig "#{kube_conf}" create -f some_k8s_deploy.yml\nkubectl --kubeconfig "#{kube_conf}" get nodes}, MU::SUMMARY
|
|
1630
|
+
end
|
|
1631
|
+
|
|
1632
|
+
def create_fargate_kubernetes_profile
|
|
1633
|
+
fargate_subnets = mySubnets.map { |s| s.cloud_id }
|
|
1634
|
+
|
|
1635
|
+
podrole_arn = @deploy.findLitterMate(name: @config['name']+"pods", type: "roles").arn
|
|
1636
|
+
poolnum = 0
|
|
1637
|
+
|
|
1638
|
+
@config['kubernetes_pools'].each { |selectors|
|
|
1639
|
+
profname = @mu_name+"-"+poolnum.to_s
|
|
1640
|
+
poolnum += 1
|
|
1641
|
+
desc = {
|
|
1642
|
+
:fargate_profile_name => profname,
|
|
1643
|
+
:cluster_name => @mu_name,
|
|
1644
|
+
:pod_execution_role_arn => podrole_arn,
|
|
1645
|
+
:selectors => selectors,
|
|
1646
|
+
:subnets => fargate_subnets.sort,
|
|
1647
|
+
:tags => @tags
|
|
1648
|
+
}
|
|
1649
|
+
begin
|
|
1650
|
+
resp = MU::Cloud::AWS.eks(region: @config['region'], credentials: @config['credentials']).describe_fargate_profile(
|
|
1651
|
+
cluster_name: @mu_name,
|
|
1652
|
+
fargate_profile_name: profname
|
|
1653
|
+
)
|
|
1654
|
+
if resp and resp.fargate_profile
|
|
1655
|
+
old_desc = MU.structToHash(resp.fargate_profile, stringify_keys: true)
|
|
1656
|
+
new_desc = MU.structToHash(desc, stringify_keys: true)
|
|
1657
|
+
["created_at", "status", "fargate_profile_arn"].each { |k|
|
|
1658
|
+
old_desc.delete(k)
|
|
1659
|
+
}
|
|
1660
|
+
old_desc["subnets"].sort!
|
|
1661
|
+
if !old_desc.eql?(new_desc)
|
|
1662
|
+
MU.log "Deleting Fargate profile #{profname} in order to apply changes", MU::WARN, details: desc
|
|
1663
|
+
MU::Cloud::AWS::ContainerCluster.purge_fargate_profile(profname, @mu_name, @config['region'], @credentials)
|
|
1664
|
+
else
|
|
1665
|
+
next
|
|
1666
|
+
end
|
|
1667
|
+
end
|
|
1668
|
+
rescue Aws::EKS::Errors::ResourceNotFoundException
|
|
1669
|
+
# This is just fine!
|
|
1670
|
+
end
|
|
1671
|
+
MU.log "Creating EKS Fargate profile #{profname}", details: desc
|
|
1672
|
+
resp = MU::Cloud::AWS.eks(region: @config['region'], credentials: @config['credentials']).create_fargate_profile(desc)
|
|
1673
|
+
begin
|
|
1674
|
+
resp = MU::Cloud::AWS.eks(region: @config['region'], credentials: @config['credentials']).describe_fargate_profile(
|
|
1675
|
+
cluster_name: @mu_name,
|
|
1676
|
+
fargate_profile_name: profname
|
|
1677
|
+
)
|
|
1678
|
+
sleep 1 if resp.fargate_profile.status == "CREATING"
|
|
1679
|
+
end while resp.fargate_profile.status == "CREATING"
|
|
1680
|
+
MU.log "Creation of EKS Fargate profile #{profname} complete"
|
|
1681
|
+
}
|
|
1682
|
+
end
|
|
1683
|
+
|
|
1684
|
+
def self.remove_kubernetes_tags(cluster, desc, region: MU.myRegion, credentials: nil, noop: false)
|
|
1685
|
+
untag = []
|
|
1686
|
+
untag << desc.resources_vpc_config.vpc_id
|
|
1687
|
+
subnets = MU::Cloud::AWS.ec2(credentials: credentials, region: region).describe_subnets(
|
|
1688
|
+
filters: [ { name: "vpc-id", values: [desc.resources_vpc_config.vpc_id] } ]
|
|
1689
|
+
).subnets
|
|
1690
|
+
|
|
1691
|
+
# subnets
|
|
1692
|
+
untag.concat(subnets.map { |s| s.subnet_id } )
|
|
1693
|
+
rtbs = MU::Cloud::AWS.ec2(credentials: credentials, region: region).describe_route_tables(
|
|
1694
|
+
filters: [ { name: "vpc-id", values: [desc.resources_vpc_config.vpc_id] } ]
|
|
1695
|
+
).route_tables
|
|
1696
|
+
untag.concat(rtbs.map { |r| r.route_table_id } )
|
|
1697
|
+
untag.concat(desc.resources_vpc_config.subnet_ids)
|
|
1698
|
+
untag.concat(desc.resources_vpc_config.security_group_ids)
|
|
1699
|
+
MU.log "Removing Kubernetes tags from VPC resources for #{cluster}", details: untag
|
|
1700
|
+
if !noop
|
|
1701
|
+
MU::Cloud::AWS.removeTag("kubernetes.io/cluster/#{cluster}", "shared", untag)
|
|
1702
|
+
MU::Cloud::AWS.removeTag("kubernetes.io/cluster/elb", cluster, untag)
|
|
1703
|
+
end
|
|
1704
|
+
end
|
|
1705
|
+
private_class_method :remove_kubernetes_tags
|
|
1706
|
+
|
|
1707
|
+
def apply_kubernetes_tags
|
|
1708
|
+
tagme = [@vpc.cloud_id]
|
|
1709
|
+
tagme_elb = []
|
|
1710
|
+
@vpc.subnets.each { |s|
|
|
1711
|
+
tagme << s.cloud_id
|
|
1712
|
+
tagme_elb << s.cloud_id if !s.private?
|
|
1713
|
+
}
|
|
1714
|
+
rtbs = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).describe_route_tables(
|
|
1715
|
+
filters: [ { name: "vpc-id", values: [@vpc.cloud_id] } ]
|
|
1716
|
+
).route_tables
|
|
1717
|
+
tagme.concat(rtbs.map { |r| r.route_table_id } )
|
|
1718
|
+
main_sg = @deploy.findLitterMate(type: "firewall_rules", name: "server_pool#{@config['name']}workers")
|
|
1719
|
+
tagme << main_sg.cloud_id if main_sg
|
|
1720
|
+
MU.log "Applying kubernetes.io tags to VPC resources", details: tagme
|
|
1721
|
+
MU::Cloud::AWS.createTag(tagme, "kubernetes.io/cluster/#{@mu_name}", "shared", credentials: @config['credentials'])
|
|
1722
|
+
MU::Cloud::AWS.createTag(tagme_elb, "kubernetes.io/cluster/elb", @mu_name, credentials: @config['credentials'])
|
|
1723
|
+
end
|
|
1724
|
+
|
|
1725
|
+
def manage_ecs_workers
|
|
1726
|
+
resp = MU::Cloud::AWS.ecs(region: @config['region'], credentials: @config['credentials']).list_container_instances({
|
|
1727
|
+
cluster: @mu_name
|
|
1728
|
+
})
|
|
1729
|
+
existing = {}
|
|
1730
|
+
if resp
|
|
1731
|
+
uuids = []
|
|
1732
|
+
resp.container_instance_arns.each { |arn|
|
|
1733
|
+
uuids << arn.sub(/^.*?:container-instance\//, "")
|
|
1734
|
+
}
|
|
1735
|
+
if uuids.size > 0
|
|
1736
|
+
resp = MU::Cloud::AWS.ecs(region: @config['region'], credentials: @config['credentials']).describe_container_instances({
|
|
1737
|
+
cluster: @mu_name,
|
|
1738
|
+
container_instances: uuids
|
|
1739
|
+
})
|
|
1740
|
+
resp.container_instances.each { |i|
|
|
1741
|
+
existing[i.ec2_instance_id] = i
|
|
1742
|
+
}
|
|
1743
|
+
end
|
|
1744
|
+
end
|
|
1745
|
+
|
|
1746
|
+
threads = []
|
|
1747
|
+
resource_lookup = MU::Cloud::AWS.listInstanceTypes(@config['region'])[@config['region']]
|
|
1748
|
+
serverpool = if ['EKS', 'ECS'].include?(@config['flavor'])
|
|
1749
|
+
@deploy.findLitterMate(type: "server_pools", name: @config["name"]+"workers")
|
|
1750
|
+
end
|
|
1751
|
+
serverpool.listNodes.each { |mynode|
|
|
1752
|
+
resources = resource_lookup[mynode.cloud_desc.instance_type]
|
|
1753
|
+
threads << Thread.new(mynode) { |node|
|
|
1754
|
+
ident_doc = nil
|
|
1755
|
+
ident_doc_sig = nil
|
|
1756
|
+
if !node.windows?
|
|
1757
|
+
session = node.getSSHSession(10, 30)
|
|
1758
|
+
ident_doc = session.exec!("curl -s http://169.254.169.254/latest/dynamic/instance-identity/document/")
|
|
1759
|
+
ident_doc_sig = session.exec!("curl -s http://169.254.169.254/latest/dynamic/instance-identity/signature/")
|
|
1760
|
+
# else
|
|
1761
|
+
# begin
|
|
1762
|
+
# session = node.getWinRMSession(1, 60)
|
|
1763
|
+
# rescue StandardError # XXX
|
|
1764
|
+
# session = node.getSSHSession(1, 60)
|
|
1765
|
+
# end
|
|
1766
|
+
end
|
|
1767
|
+
MU.log "Identity document for #{node}", MU::DEBUG, details: ident_doc
|
|
1768
|
+
MU.log "Identity document signature for #{node}", MU::DEBUG, details: ident_doc_sig
|
|
1769
|
+
params = {
|
|
1770
|
+
:cluster => @mu_name,
|
|
1771
|
+
:instance_identity_document => ident_doc,
|
|
1772
|
+
:instance_identity_document_signature => ident_doc_sig,
|
|
1773
|
+
:total_resources => [
|
|
1774
|
+
{
|
|
1775
|
+
:name => "CPU",
|
|
1776
|
+
:type => "INTEGER",
|
|
1777
|
+
:integer_value => resources["vcpu"].to_i
|
|
1778
|
+
},
|
|
1779
|
+
{
|
|
1780
|
+
:name => "MEMORY",
|
|
1781
|
+
:type => "INTEGER",
|
|
1782
|
+
:integer_value => (resources["memory"]*1024*1024).to_i
|
|
1783
|
+
}
|
|
1784
|
+
]
|
|
1785
|
+
}
|
|
1786
|
+
if !existing.has_key?(node.cloud_id)
|
|
1787
|
+
MU.log "Registering ECS instance #{node} in cluster #{@mu_name}", details: params
|
|
1788
|
+
else
|
|
1789
|
+
params[:container_instance_arn] = existing[node.cloud_id].container_instance_arn
|
|
1790
|
+
MU.log "Updating ECS instance #{node} in cluster #{@mu_name}", MU::NOTICE, details: params
|
|
1791
|
+
end
|
|
1792
|
+
MU::Cloud::AWS.ecs(region: @config['region'], credentials: @config['credentials']).register_container_instance(params)
|
|
1793
|
+
|
|
1794
|
+
}
|
|
1795
|
+
}
|
|
1796
|
+
threads.each { |t|
|
|
1797
|
+
t.join
|
|
1798
|
+
}
|
|
1799
|
+
end
|
|
1800
|
+
|
|
1801
|
+
def get_ecs_loadbalancers(container_name)
|
|
1802
|
+
lbs = []
|
|
1803
|
+
|
|
1804
|
+
if @loadbalancers and !@loadbalancers.empty?
|
|
1805
|
+
@loadbalancers.each {|lb|
|
|
1806
|
+
MU.log "Mapping LB #{lb.mu_name} to service #{c['name']}", MU::INFO
|
|
1807
|
+
if lb.cloud_desc.type != "classic"
|
|
1808
|
+
elb_groups = MU::Cloud::AWS.elb2(region: @config['region'], credentials: @config['credentials']).describe_target_groups({
|
|
1809
|
+
load_balancer_arn: lb.cloud_desc.load_balancer_arn
|
|
1810
|
+
})
|
|
1811
|
+
matching_target_groups = []
|
|
1812
|
+
elb_groups.target_groups.each { |tg|
|
|
1813
|
+
if tg.port.to_i == lb['container_port'].to_i
|
|
1814
|
+
matching_target_groups << {
|
|
1815
|
+
arn: tg['target_group_arn'],
|
|
1816
|
+
name: tg['target_group_name']
|
|
1817
|
+
}
|
|
1818
|
+
end
|
|
1819
|
+
}
|
|
1820
|
+
if matching_target_groups.length >= 1
|
|
1821
|
+
MU.log "#{matching_target_groups.length} matching target groups lb. Mapping #{container_name} to target group #{matching_target_groups.first['name']}", MU::INFO
|
|
1822
|
+
lbs << {
|
|
1823
|
+
container_name: container_name,
|
|
1824
|
+
container_port: lb['container_port'],
|
|
1825
|
+
target_group_arn: matching_target_groups.first[:arn]
|
|
1826
|
+
}
|
|
1827
|
+
else
|
|
1828
|
+
raise MuError, "No matching target groups lb"
|
|
1829
|
+
end
|
|
1830
|
+
elsif @config['flavor'] == "Fargate" && lb.cloud_desc.type == "classic"
|
|
1831
|
+
raise MuError, "Classic Load Balancers are not supported with Fargate."
|
|
1832
|
+
else
|
|
1833
|
+
MU.log "Mapping Classic LB #{lb.mu_name} to service #{container_name}", MU::INFO
|
|
1834
|
+
lbs << {
|
|
1835
|
+
container_name: container_name,
|
|
1836
|
+
container_port: lb['container_port'],
|
|
1837
|
+
load_balancer_name: lb.mu_name
|
|
1838
|
+
}
|
|
1839
|
+
end
|
|
1840
|
+
}
|
|
1841
|
+
end
|
|
1842
|
+
|
|
1843
|
+
lbs
|
|
1844
|
+
end
|
|
1845
|
+
|
|
1846
|
+
def get_ecs_container_definitions(containers)
|
|
1847
|
+
role_arn = nil
|
|
1848
|
+
lbs = []
|
|
1849
|
+
|
|
1850
|
+
defs = containers.map { |c|
|
|
1851
|
+
container_name = @mu_name+"-"+c['name'].upcase
|
|
1852
|
+
lbs.concat(get_ecs_loadbalancers(container_name))
|
|
1853
|
+
|
|
1854
|
+
if c["role"] and !role_arn
|
|
1855
|
+
found = MU::MommaCat.findStray(
|
|
1856
|
+
@config['cloud'],
|
|
1857
|
+
"role",
|
|
1858
|
+
cloud_id: c["role"]["id"],
|
|
1859
|
+
name: c["role"]["name"],
|
|
1860
|
+
deploy_id: c["role"]["deploy_id"] || @deploy.deploy_id,
|
|
1861
|
+
dummy_ok: false
|
|
1862
|
+
)
|
|
1863
|
+
if found
|
|
1864
|
+
found = found.first
|
|
1865
|
+
if found and found.cloudobj
|
|
1866
|
+
role_arn = found.cloudobj.arn
|
|
1867
|
+
end
|
|
1868
|
+
else
|
|
1869
|
+
raise MuError, "Unable to find execution role from #{c["role"]}"
|
|
1870
|
+
end
|
|
1871
|
+
end
|
|
1872
|
+
|
|
1873
|
+
params = {
|
|
1874
|
+
name: @mu_name+"-"+c['name'].upcase,
|
|
1875
|
+
image: c['image'],
|
|
1876
|
+
memory: c['memory'],
|
|
1877
|
+
cpu: c['cpu']
|
|
1878
|
+
}
|
|
1879
|
+
if !@config['vpc']
|
|
1880
|
+
c['hostname'] ||= @mu_name+"-"+c['name'].upcase
|
|
1881
|
+
end
|
|
1882
|
+
[:essential, :hostname, :start_timeout, :stop_timeout, :user, :working_directory, :disable_networking, :privileged, :readonly_root_filesystem, :interactive, :pseudo_terminal, :links, :entry_point, :command, :dns_servers, :dns_search_domains, :docker_security_options, :port_mappings, :repository_credentials, :mount_points, :environment, :volumes_from, :secrets, :depends_on, :extra_hosts, :docker_labels, :ulimits, :system_controls, :health_check, :resource_requirements].each { |param|
|
|
1883
|
+
if c.has_key?(param.to_s)
|
|
1884
|
+
params[param] = if !c[param.to_s].nil? and (c[param.to_s].is_a?(Hash) or c[param.to_s].is_a?(Array))
|
|
1885
|
+
MU.strToSym(c[param.to_s])
|
|
1886
|
+
else
|
|
1887
|
+
c[param.to_s]
|
|
1888
|
+
end
|
|
1889
|
+
end
|
|
1890
|
+
}
|
|
1891
|
+
if @config['vpc']
|
|
1892
|
+
[:hostname, :dns_servers, :dns_search_domains, :links].each { |param|
|
|
1893
|
+
if params[param]
|
|
1894
|
+
MU.log "Container parameter #{param.to_s} not supported in VPC clusters, ignoring", MU::WARN
|
|
1895
|
+
params.delete(param)
|
|
1896
|
+
end
|
|
1897
|
+
}
|
|
1898
|
+
end
|
|
1899
|
+
if @config['flavor'] == "Fargate"
|
|
1900
|
+
[:privileged, :docker_security_options].each { |param|
|
|
1901
|
+
if params[param]
|
|
1902
|
+
MU.log "Container parameter #{param.to_s} not supported in Fargate clusters, ignoring", MU::WARN
|
|
1903
|
+
params.delete(param)
|
|
1904
|
+
end
|
|
1905
|
+
}
|
|
1906
|
+
end
|
|
1907
|
+
if c['log_configuration']
|
|
1908
|
+
log_obj = @deploy.findLitterMate(name: c['log_configuration']['options']['awslogs-group'], type: "logs")
|
|
1909
|
+
if log_obj
|
|
1910
|
+
c['log_configuration']['options']['awslogs-group'] = log_obj.mu_name
|
|
1911
|
+
end
|
|
1912
|
+
params[:log_configuration] = MU.strToSym(c['log_configuration'])
|
|
1913
|
+
end
|
|
1914
|
+
params
|
|
1915
|
+
}
|
|
1916
|
+
|
|
1917
|
+
[defs, role_arn, lbs]
|
|
1918
|
+
end
|
|
1919
|
+
|
|
1920
|
+
def register_ecs_task(container_definitions, service_name, cpu_total = 2, mem_total = 2, role_arn: nil)
|
|
1921
|
+
task_params = {
|
|
1922
|
+
family: @deploy.deploy_id,
|
|
1923
|
+
container_definitions: container_definitions,
|
|
1924
|
+
requires_compatibilities: [@config['flavor'] == "ECS" ? "EC2" : "FARGATE"]
|
|
1925
|
+
}
|
|
1926
|
+
|
|
1927
|
+
if @config['volumes']
|
|
1928
|
+
task_params[:volumes] = []
|
|
1929
|
+
@config['volumes'].each { |v|
|
|
1930
|
+
vol = { :name => v['name'] }
|
|
1931
|
+
if v['type'] == "host"
|
|
1932
|
+
vol[:host] = {}
|
|
1933
|
+
if v['host_volume_source_path']
|
|
1934
|
+
vol[:host][:source_path] = v['host_volume_source_path']
|
|
1935
|
+
end
|
|
1936
|
+
elsif v['type'] == "docker"
|
|
1937
|
+
vol[:docker_volume_configuration] = MU.strToSym(v['docker_volume_configuration'])
|
|
1938
|
+
else
|
|
1939
|
+
raise MuError, "Invalid volume type '#{v['type']}' specified in ContainerCluster '#{@mu_name}'"
|
|
1940
|
+
end
|
|
1941
|
+
task_params[:volumes] << vol
|
|
1942
|
+
}
|
|
1943
|
+
end
|
|
1944
|
+
|
|
1945
|
+
if role_arn
|
|
1946
|
+
task_params[:execution_role_arn] = role_arn
|
|
1947
|
+
task_params[:task_role_arn] = role_arn
|
|
1948
|
+
end
|
|
1949
|
+
if @config['flavor'] == "Fargate"
|
|
1950
|
+
task_params[:network_mode] = "awsvpc"
|
|
1951
|
+
task_params[:cpu] = cpu_total.to_i.to_s
|
|
1952
|
+
task_params[:memory] = mem_total.to_i.to_s
|
|
1953
|
+
elsif @config['vpc']
|
|
1954
|
+
task_params[:network_mode] = "awsvpc"
|
|
1955
|
+
end
|
|
1956
|
+
|
|
1957
|
+
MU.log "Registering task definition #{service_name} with #{container_definitions.size.to_s} containers"
|
|
1958
|
+
|
|
1959
|
+
# XXX this helpfully keeps revisions, but let's compare anyway and avoid cluttering with identical ones
|
|
1960
|
+
resp = MU::Cloud::AWS.ecs(region: @config['region'], credentials: @config['credentials']).register_task_definition(task_params)
|
|
1961
|
+
|
|
1962
|
+
resp.task_definition.task_definition_arn
|
|
1963
|
+
end
|
|
1964
|
+
|
|
1965
|
+
def list_ecs_services
|
|
1966
|
+
svc_resp = nil
|
|
1967
|
+
MU.retrier([Aws::ECS::Errors::ClusterNotFoundException], wait: 5, max: 10){
|
|
1968
|
+
svc_resp = MU::Cloud::AWS.ecs(region: @config['region'], credentials: @config['credentials']).list_services(
|
|
1969
|
+
cluster: arn
|
|
1970
|
+
)
|
|
1971
|
+
}
|
|
1972
|
+
|
|
1973
|
+
svc_resp.service_arns.map { |s|
|
|
1974
|
+
s.gsub(/.*?:service\/(.*)/, '\1')
|
|
1975
|
+
}
|
|
1976
|
+
end
|
|
1977
|
+
|
|
1978
|
+
def create_update_ecs_service(task_def, service_name, lbs, existing_svcs)
|
|
1979
|
+
service_params = {
|
|
1980
|
+
:cluster => @mu_name,
|
|
1981
|
+
:desired_count => @config['instance_count'], # XXX this makes no sense
|
|
1982
|
+
:service_name => service_name,
|
|
1983
|
+
:launch_type => @config['flavor'] == "ECS" ? "EC2" : "FARGATE",
|
|
1984
|
+
:task_definition => task_def,
|
|
1985
|
+
:load_balancers => lbs
|
|
1986
|
+
}
|
|
1987
|
+
if @config['vpc']
|
|
1988
|
+
subnet_ids = []
|
|
1989
|
+
all_public = true
|
|
1990
|
+
|
|
1991
|
+
mySubnets.each { |subnet|
|
|
1992
|
+
subnet_ids << subnet.cloud_id
|
|
1993
|
+
all_public = false if subnet.private?
|
|
1994
|
+
}
|
|
1995
|
+
|
|
1996
|
+
service_params[:network_configuration] = {
|
|
1997
|
+
:awsvpc_configuration => {
|
|
1998
|
+
:subnets => subnet_ids,
|
|
1999
|
+
:security_groups => myFirewallRules.map { |fw| fw.cloud_id },
|
|
2000
|
+
:assign_public_ip => all_public ? "ENABLED" : "DISABLED"
|
|
2001
|
+
}
|
|
2002
|
+
}
|
|
2003
|
+
end
|
|
2004
|
+
|
|
2005
|
+
if !existing_svcs.include?(service_name)
|
|
2006
|
+
MU.log "Creating Service #{service_name}"
|
|
2007
|
+
|
|
2008
|
+
MU::Cloud::AWS.ecs(region: @config['region'], credentials: @config['credentials']).create_service(service_params)
|
|
2009
|
+
else
|
|
2010
|
+
service_params[:service] = service_params[:service_name].dup
|
|
2011
|
+
service_params.delete(:service_name)
|
|
2012
|
+
service_params.delete(:launch_type)
|
|
2013
|
+
MU.log "Updating Service #{service_name}", MU::NOTICE, details: service_params
|
|
2014
|
+
|
|
2015
|
+
MU::Cloud::AWS.ecs(region: @config['region'], credentials: @config['credentials']).update_service(service_params)
|
|
2016
|
+
end
|
|
2037
2017
|
end
|
|
2038
2018
|
|
|
2039
2019
|
end
|