ecs_deploy_cli 0.2.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/ecs_deploy_cli.rb +1 -0
- data/lib/ecs_deploy_cli/cli.rb +11 -1
- data/lib/ecs_deploy_cli/cloudformation/default.yml +411 -0
- data/lib/ecs_deploy_cli/dsl/cluster.rb +70 -0
- data/lib/ecs_deploy_cli/dsl/container.rb +4 -0
- data/lib/ecs_deploy_cli/dsl/parser.rb +6 -2
- data/lib/ecs_deploy_cli/dsl/service.rb +31 -2
- data/lib/ecs_deploy_cli/runner.rb +7 -2
- data/lib/ecs_deploy_cli/runners/base.rb +61 -0
- data/lib/ecs_deploy_cli/runners/diff.rb +38 -8
- data/lib/ecs_deploy_cli/runners/logs.rb +14 -0
- data/lib/ecs_deploy_cli/runners/setup.rb +162 -0
- data/lib/ecs_deploy_cli/runners/ssh.rb +44 -10
- data/lib/ecs_deploy_cli/runners/status.rb +23 -0
- data/lib/ecs_deploy_cli/runners/update_crons.rb +16 -7
- data/lib/ecs_deploy_cli/version.rb +1 -1
- data/spec/ecs_deploy_cli/cli_spec.rb +8 -0
- data/spec/ecs_deploy_cli/dsl/cluster_spec.rb +48 -0
- data/spec/ecs_deploy_cli/dsl/container_spec.rb +2 -0
- data/spec/ecs_deploy_cli/dsl/cron_spec.rb +2 -0
- data/spec/ecs_deploy_cli/dsl/parser_spec.rb +2 -0
- data/spec/ecs_deploy_cli/dsl/service_spec.rb +31 -0
- data/spec/ecs_deploy_cli/runner_spec.rb +164 -23
- data/spec/ecs_deploy_cli/runners/base_spec.rb +57 -0
- data/spec/spec_helper.rb +2 -3
- data/spec/support/ECSFile +13 -1
- metadata +72 -5
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module EcsDeployCli
|
4
|
+
module DSL
|
5
|
+
class Cluster
|
6
|
+
include AutoOptions
|
7
|
+
|
8
|
+
allowed_options :instances_count, :instance_type, :ebs_volume_size, :keypair_name
|
9
|
+
|
10
|
+
def initialize(name, config)
|
11
|
+
@config = config
|
12
|
+
_options[:name] = name.to_s
|
13
|
+
end
|
14
|
+
|
15
|
+
def vpc(id = nil, &block)
|
16
|
+
@vpc = VPC.new(id)
|
17
|
+
@vpc.instance_exec(&block)
|
18
|
+
end
|
19
|
+
|
20
|
+
def as_definition
|
21
|
+
{
|
22
|
+
instances_count: 1,
|
23
|
+
|
24
|
+
device_name: '/dev/xvda',
|
25
|
+
ebs_volume_size: 22,
|
26
|
+
ebs_volume_type: 'gp2',
|
27
|
+
|
28
|
+
root_device_name: '/dev/xvdcz',
|
29
|
+
root_ebs_volume_size: 30,
|
30
|
+
|
31
|
+
vpc: @vpc&.as_definition
|
32
|
+
}.merge(_options)
|
33
|
+
end
|
34
|
+
|
35
|
+
class VPC
|
36
|
+
include AutoOptions
|
37
|
+
allowed_options :cidr, :subnet1, :subnet2, :subnet3
|
38
|
+
|
39
|
+
def initialize(id)
|
40
|
+
_options[:id] = id
|
41
|
+
end
|
42
|
+
|
43
|
+
def availability_zones(*values)
|
44
|
+
_options[:availability_zones] = values.join(',')
|
45
|
+
end
|
46
|
+
|
47
|
+
def subnet_ids(*values)
|
48
|
+
_options[:subnet_ids] = values.join(',')
|
49
|
+
end
|
50
|
+
|
51
|
+
def as_definition
|
52
|
+
validate! if _options[:id]
|
53
|
+
|
54
|
+
{
|
55
|
+
cidr: '10.0.0.0/16',
|
56
|
+
subnet1: '10.0.0.0/24',
|
57
|
+
subnet2: '10.0.1.0/24',
|
58
|
+
subnet3: '10.0.2.0/24'
|
59
|
+
}.merge(_options)
|
60
|
+
end
|
61
|
+
|
62
|
+
def validate!
|
63
|
+
[
|
64
|
+
:subnet1, :subnet_ids, :availability_zones
|
65
|
+
].each { |key| raise "Missing required parameter #{key}" unless _options[key] }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -40,8 +40,11 @@ module EcsDeployCli
|
|
40
40
|
@crons[name].instance_exec(&block)
|
41
41
|
end
|
42
42
|
|
43
|
-
def cluster(name)
|
43
|
+
def cluster(name, &block)
|
44
44
|
config[:cluster] = name
|
45
|
+
@cluster ||= {}.with_indifferent_access
|
46
|
+
@cluster = Cluster.new(name, config)
|
47
|
+
@cluster.instance_exec(&block) if block
|
45
48
|
end
|
46
49
|
|
47
50
|
def config
|
@@ -58,7 +61,8 @@ module EcsDeployCli
|
|
58
61
|
resolved_containers = (@containers || {}).transform_values(&:as_definition)
|
59
62
|
resolved_tasks = (@tasks || {}).transform_values { |t| t.as_definition(resolved_containers) }
|
60
63
|
resolved_crons = (@crons || {}).transform_values { |t| t.as_definition(resolved_tasks) }
|
61
|
-
|
64
|
+
resolved_cluster = @cluster.as_definition
|
65
|
+
[@services, resolved_tasks, resolved_crons, resolved_cluster]
|
62
66
|
end
|
63
67
|
|
64
68
|
def self.load(file)
|
@@ -18,13 +18,42 @@ module EcsDeployCli
|
|
18
18
|
_options
|
19
19
|
end
|
20
20
|
|
21
|
+
def load_balancer(name, &block)
|
22
|
+
@load_balancers ||= []
|
23
|
+
|
24
|
+
load_balancer = LoadBalancer.new(name, @config)
|
25
|
+
load_balancer.instance_exec(&block)
|
26
|
+
|
27
|
+
@load_balancers << load_balancer
|
28
|
+
end
|
29
|
+
|
21
30
|
def as_definition(task)
|
22
31
|
{
|
23
32
|
cluster: @config[:cluster],
|
24
|
-
service:
|
25
|
-
task_definition: task
|
33
|
+
service: _options[:service],
|
34
|
+
task_definition: task,
|
35
|
+
load_balancers: @load_balancers&.map(&:as_definition) || []
|
26
36
|
}
|
27
37
|
end
|
38
|
+
|
39
|
+
class LoadBalancer
|
40
|
+
include AutoOptions
|
41
|
+
allowed_options :container_name, :container_port
|
42
|
+
|
43
|
+
def initialize(name, config)
|
44
|
+
_options[:load_balancer_name] = name
|
45
|
+
@config = config
|
46
|
+
end
|
47
|
+
|
48
|
+
def target_group_arn(value)
|
49
|
+
_options[:target_group_arn] = "arn:aws:elasticloadbalancing:#{@config[:aws_region]}:#{@config[:aws_profile_id]}:targetgroup/#{value}"
|
50
|
+
_options.delete(:load_balancer_name)
|
51
|
+
end
|
52
|
+
|
53
|
+
def as_definition
|
54
|
+
_options
|
55
|
+
end
|
56
|
+
end
|
28
57
|
end
|
29
58
|
end
|
30
59
|
end
|
@@ -7,6 +7,7 @@ require 'ecs_deploy_cli/runners/diff'
|
|
7
7
|
require 'ecs_deploy_cli/runners/update_crons'
|
8
8
|
require 'ecs_deploy_cli/runners/update_services'
|
9
9
|
require 'ecs_deploy_cli/runners/run_task'
|
10
|
+
require 'ecs_deploy_cli/runners/setup'
|
10
11
|
|
11
12
|
module EcsDeployCli
|
12
13
|
class Runner
|
@@ -14,6 +15,10 @@ module EcsDeployCli
|
|
14
15
|
@parser = parser
|
15
16
|
end
|
16
17
|
|
18
|
+
def setup!
|
19
|
+
EcsDeployCli::Runners::Setup.new(@parser).run!
|
20
|
+
end
|
21
|
+
|
17
22
|
def validate!
|
18
23
|
EcsDeployCli::Runners::Validate.new(@parser).run!
|
19
24
|
end
|
@@ -26,8 +31,8 @@ module EcsDeployCli
|
|
26
31
|
EcsDeployCli::Runners::RunTask.new(@parser).run!(task_name, launch_type: launch_type, security_groups: security_groups, subnets: subnets)
|
27
32
|
end
|
28
33
|
|
29
|
-
def ssh
|
30
|
-
EcsDeployCli::Runners::SSH.new(@parser).run!
|
34
|
+
def ssh(**options)
|
35
|
+
EcsDeployCli::Runners::SSH.new(@parser).run!(options)
|
31
36
|
end
|
32
37
|
|
33
38
|
def diff
|
@@ -11,14 +11,35 @@ module EcsDeployCli
|
|
11
11
|
raise NotImplementedError, 'abstract method'
|
12
12
|
end
|
13
13
|
|
14
|
+
def update_task(definition)
|
15
|
+
_update_task(definition)
|
16
|
+
end
|
17
|
+
|
14
18
|
protected
|
15
19
|
|
16
20
|
def _update_task(definition)
|
21
|
+
definition[:container_definitions].each do |container|
|
22
|
+
next unless container.dig(:log_configuration, :log_driver) == 'awslogs'
|
23
|
+
|
24
|
+
_create_cloudwatch_logs_if_needed(container.dig(:log_configuration, :options, 'awslogs-group'))
|
25
|
+
end
|
26
|
+
|
17
27
|
ecs_client.register_task_definition(
|
18
28
|
definition
|
19
29
|
).to_h[:task_definition]
|
20
30
|
end
|
21
31
|
|
32
|
+
def _create_cloudwatch_logs_if_needed(prefix)
|
33
|
+
log_group = cwl_client.describe_log_groups(log_group_name_prefix: prefix, limit: 1).to_h[:log_groups]
|
34
|
+
return if log_group.any?
|
35
|
+
|
36
|
+
cwl_client.create_log_group(log_group_name: prefix)
|
37
|
+
cwl_client.put_retention_policy(
|
38
|
+
log_group_name: prefix,
|
39
|
+
retention_in_days: 14
|
40
|
+
)
|
41
|
+
end
|
42
|
+
|
22
43
|
def ec2_client
|
23
44
|
@ec2_client ||= begin
|
24
45
|
require 'aws-sdk-ec2'
|
@@ -46,6 +67,46 @@ module EcsDeployCli
|
|
46
67
|
end
|
47
68
|
end
|
48
69
|
|
70
|
+
def ssm_client
|
71
|
+
@cwl_client ||= begin
|
72
|
+
require 'aws-sdk-ssm'
|
73
|
+
Aws::SSM::Client.new(
|
74
|
+
profile: ENV.fetch('AWS_PROFILE', 'default'),
|
75
|
+
region: config[:aws_region]
|
76
|
+
)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def cwl_client
|
81
|
+
@cwl_client ||= begin
|
82
|
+
require 'aws-sdk-cloudwatchlogs'
|
83
|
+
Aws::CloudWatchLogs::Client.new(
|
84
|
+
profile: ENV.fetch('AWS_PROFILE', 'default'),
|
85
|
+
region: config[:aws_region]
|
86
|
+
)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def cf_client
|
91
|
+
@cl_client ||= begin
|
92
|
+
require 'aws-sdk-cloudformation'
|
93
|
+
Aws::CloudFormation::Client.new(
|
94
|
+
profile: ENV.fetch('AWS_PROFILE', 'default'),
|
95
|
+
region: config[:aws_region]
|
96
|
+
)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def iam_client
|
101
|
+
@iam_client ||= begin
|
102
|
+
require 'aws-sdk-iam'
|
103
|
+
Aws::IAM::Client.new(
|
104
|
+
profile: ENV.fetch('AWS_PROFILE', 'default'),
|
105
|
+
region: config[:aws_region]
|
106
|
+
)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
49
110
|
def config
|
50
111
|
@parser.config
|
51
112
|
end
|
@@ -15,27 +15,57 @@ module EcsDeployCli
|
|
15
15
|
|
16
16
|
result = ecs_client.describe_task_definition(task_definition: task_name).to_h
|
17
17
|
|
18
|
-
current = result[:task_definition]
|
18
|
+
current = cleanup_source_task(result[:task_definition])
|
19
|
+
definition = cleanup_source_task(definition)
|
19
20
|
|
20
21
|
print_diff Hashdiff.diff(current.except(:container_definitions), definition.except(:container_definitions))
|
21
22
|
|
22
|
-
|
23
|
-
|
23
|
+
diff_container_definitions(
|
24
|
+
current[:container_definitions],
|
25
|
+
definition[:container_definitions]
|
26
|
+
)
|
24
27
|
|
25
|
-
print_diff Hashdiff.diff(a, b) if a && b
|
26
|
-
end
|
27
28
|
EcsDeployCli.logger.info '---'
|
28
29
|
end
|
29
30
|
end
|
30
31
|
|
32
|
+
private
|
33
|
+
|
34
|
+
def diff_container_definitions(first, second)
|
35
|
+
first.zip(second).each do |a, b|
|
36
|
+
EcsDeployCli.logger.info "Container #{a&.dig(:name) || 'NONE'} <=> #{b&.dig(:name) || 'NONE'}"
|
37
|
+
|
38
|
+
next if !a || !b
|
39
|
+
|
40
|
+
sort_envs! a
|
41
|
+
sort_envs! b
|
42
|
+
|
43
|
+
print_diff Hashdiff.diff(a.delete_if { |_, v| v.nil? }, b.delete_if { |_, v| v.nil? })
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def sort_envs!(definition)
|
48
|
+
return unless definition[:environment]
|
49
|
+
|
50
|
+
definition[:environment].sort_by! { |e| e[:name] }
|
51
|
+
end
|
52
|
+
|
53
|
+
def cleanup_source_task(task)
|
54
|
+
task.except(
|
55
|
+
:revision, :compatibilities, :status, :registered_at, :registered_by,
|
56
|
+
:requires_attributes, :task_definition_arn
|
57
|
+
).delete_if { |_, v| v.nil? }
|
58
|
+
end
|
59
|
+
|
31
60
|
def print_diff(diff)
|
32
61
|
diff.each do |(op, path, *values)|
|
33
|
-
|
62
|
+
case op
|
63
|
+
when '-'
|
34
64
|
EcsDeployCli.logger.info "#{op} #{path} => #{values.join(' ')}".colorize(:red)
|
35
|
-
|
65
|
+
when '+'
|
36
66
|
EcsDeployCli.logger.info "#{op} #{path} => #{values.join(' ')}".colorize(:green)
|
37
67
|
else
|
38
|
-
EcsDeployCli.logger.info "#{op} #{path} => #{values.join(' ')}".colorize(:
|
68
|
+
EcsDeployCli.logger.info "#{op} #{path} => #{values.join(' ')}".colorize(:yellow)
|
39
69
|
end
|
40
70
|
end
|
41
71
|
end
|
@@ -0,0 +1,162 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module EcsDeployCli
|
4
|
+
module Runners
|
5
|
+
class Setup < Base
|
6
|
+
REQUIRED_ECS_ROLES = {
|
7
|
+
'ecsInstanceRole' => 'https://docs.aws.amazon.com/batch/latest/userguide/instance_IAM_role.html',
|
8
|
+
'ecsTaskExecutionRole' => 'https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_execution_IAM_role.html'
|
9
|
+
}.freeze
|
10
|
+
class SetupError < StandardError; end
|
11
|
+
|
12
|
+
def run!
|
13
|
+
services, resolved_tasks, _, cluster_options = @parser.resolve
|
14
|
+
|
15
|
+
ensure_ecs_roles_exists!
|
16
|
+
|
17
|
+
setup_cluster! cluster_options
|
18
|
+
setup_services! services, resolved_tasks: resolved_tasks
|
19
|
+
rescue SetupError => e
|
20
|
+
EcsDeployCli.logger.info e.message
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def setup_cluster!(cluster_options)
|
26
|
+
if cluster_exists?
|
27
|
+
EcsDeployCli.logger.info 'Cluster already created, skipping.'
|
28
|
+
return
|
29
|
+
end
|
30
|
+
|
31
|
+
EcsDeployCli.logger.info "Creating cluster #{config[:cluster]}..."
|
32
|
+
|
33
|
+
create_keypair_if_required! cluster_options
|
34
|
+
params = create_params(cluster_options)
|
35
|
+
|
36
|
+
ecs_client.create_cluster(
|
37
|
+
cluster_name: config[:cluster]
|
38
|
+
)
|
39
|
+
EcsDeployCli.logger.info 'Cluster created, now running cloudformation...'
|
40
|
+
|
41
|
+
stack_name = "EC2ContainerService-#{config[:cluster]}"
|
42
|
+
|
43
|
+
cf_client.create_stack(
|
44
|
+
stack_name: stack_name,
|
45
|
+
template_body: File.read(File.join(__dir__, '..', 'cloudformation', 'default.yml')),
|
46
|
+
on_failure: 'ROLLBACK',
|
47
|
+
parameters: format_cloudformation_params(params)
|
48
|
+
)
|
49
|
+
|
50
|
+
cf_client.wait_until(:stack_create_complete, { stack_name: stack_name }, delay: 30, max_attempts: 120)
|
51
|
+
EcsDeployCli.logger.info "Cluster #{config[:cluster]} created! 🎉"
|
52
|
+
end
|
53
|
+
|
54
|
+
def setup_services!(services, resolved_tasks:)
|
55
|
+
services.each do |service_name, service_definition|
|
56
|
+
existing_services = ecs_client.describe_services(cluster: config[:cluster], services: [service_name]).to_h[:services].filter { |s| s[:status] != 'INACTIVE' }
|
57
|
+
if existing_services.any?
|
58
|
+
EcsDeployCli.logger.info "Service #{service_name} already created, skipping."
|
59
|
+
next
|
60
|
+
end
|
61
|
+
|
62
|
+
EcsDeployCli.logger.info "Creating service #{service_name}..."
|
63
|
+
task_definition = _update_task resolved_tasks[service_definition.options[:task]]
|
64
|
+
task_name = "#{task_definition[:family]}:#{task_definition[:revision]}"
|
65
|
+
|
66
|
+
ecs_client.create_service(
|
67
|
+
cluster: config[:cluster],
|
68
|
+
desired_count: 1, # FIXME: this should be a parameter
|
69
|
+
load_balancers: service_definition.as_definition(task_definition)[:load_balancers],
|
70
|
+
service_name: service_name,
|
71
|
+
task_definition: task_name
|
72
|
+
)
|
73
|
+
EcsDeployCli.logger.info "Service #{service_name} created!"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def create_params(cluster_options)
|
78
|
+
raise ArgumentError, 'Missing vpc configuration' unless cluster_options[:vpc]
|
79
|
+
|
80
|
+
{
|
81
|
+
'AsgMaxSize' => cluster_options[:instances_count],
|
82
|
+
'AutoAssignPublicIp' => 'INHERIT',
|
83
|
+
'ConfigureDataVolume' => false,
|
84
|
+
'ConfigureRootVolume' => true,
|
85
|
+
'DeviceName' => cluster_options[:device_name],
|
86
|
+
'EbsVolumeSize' => cluster_options[:ebs_volume_size],
|
87
|
+
'EbsVolumeType' => cluster_options[:ebs_volume_type],
|
88
|
+
'EcsAmiId' => load_ami_id,
|
89
|
+
'EcsClusterName' => config[:cluster],
|
90
|
+
'EcsEndpoint' => nil,
|
91
|
+
'EcsInstanceType' => cluster_options[:instance_type],
|
92
|
+
'IamRoleInstanceProfile' => "arn:aws:iam::#{config[:aws_profile_id]}:instance-profile/ecsInstanceRole",
|
93
|
+
'IamSpotFleetRoleArn' => nil,
|
94
|
+
'IsWindows' => false,
|
95
|
+
'KeyName' => cluster_options[:keypair_name],
|
96
|
+
'RootDeviceName' => cluster_options[:root_device_name],
|
97
|
+
'RootEbsVolumeSize' => cluster_options[:root_ebs_volume_size],
|
98
|
+
|
99
|
+
##### TODO: Implement this feature
|
100
|
+
'SecurityGroupId' => nil,
|
101
|
+
'SecurityIngressCidrIp' => '0.0.0.0/0',
|
102
|
+
'SecurityIngressFromPort' => 80,
|
103
|
+
'SecurityIngressToPort' => 80,
|
104
|
+
#####
|
105
|
+
|
106
|
+
##### TODO: Implement this feature
|
107
|
+
'SpotAllocationStrategy' => 'diversified',
|
108
|
+
'SpotPrice' => nil,
|
109
|
+
'UseSpot' => false,
|
110
|
+
#####
|
111
|
+
|
112
|
+
'UserData' => "#!/bin/bash\necho ECS_CLUSTER=#{config[:cluster]} >> /etc/ecs/ecs.config;echo ECS_BACKEND_HOST= >> /etc/ecs/ecs.config;",
|
113
|
+
'VpcAvailabilityZones' => cluster_options.dig(:vpc, :availability_zones),
|
114
|
+
'VpcCidr' => cluster_options.dig(:vpc, :cidr),
|
115
|
+
'SubnetCidr1' => cluster_options.dig(:vpc, :subnet1),
|
116
|
+
'SubnetCidr2' => cluster_options.dig(:vpc, :subnet2),
|
117
|
+
'SubnetCidr3' => cluster_options.dig(:vpc, :subnet3),
|
118
|
+
|
119
|
+
'VpcId' => cluster_options.dig(:vpc, :id),
|
120
|
+
'SubnetIds' => cluster_options.dig(:vpc, :subnet_ids)
|
121
|
+
}
|
122
|
+
end
|
123
|
+
|
124
|
+
def cluster_exists?
|
125
|
+
clusters = ecs_client.describe_clusters(clusters: [config[:cluster]]).to_h[:clusters]
|
126
|
+
|
127
|
+
clusters.filter { |c| c[:status] != 'INACTIVE' }.length == 1
|
128
|
+
end
|
129
|
+
|
130
|
+
def ensure_ecs_roles_exists!
|
131
|
+
REQUIRED_ECS_ROLES.each do |role_name, link|
|
132
|
+
iam_client.get_role(role_name: role_name).to_h
|
133
|
+
rescue Aws::IAM::Errors::NoSuchEntity
|
134
|
+
raise SetupError, "IAM Role #{role_name} does not exist. Please create it: #{link}."
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def create_keypair_if_required!(cluster_options)
|
139
|
+
ec2_client.describe_key_pairs(key_names: [cluster_options[:keypair_name]]).to_h[:key_pairs]
|
140
|
+
rescue Aws::EC2::Errors::InvalidKeyPairNotFound
|
141
|
+
EcsDeployCli.logger.info "Keypair \"#{cluster_options[:keypair_name]}\" not found, creating it..."
|
142
|
+
key_material = ec2_client.create_key_pair(key_name: cluster_options[:keypair_name]).to_h[:key_material]
|
143
|
+
File.write("#{cluster_options[:keypair_name]}.pem", key_material)
|
144
|
+
EcsDeployCli.logger.info "Created PEM file at #{Dir.pwd}/#{cluster_options[:keypair_name]}.pem"
|
145
|
+
end
|
146
|
+
|
147
|
+
def format_cloudformation_params(params)
|
148
|
+
params.map { |k, v| { parameter_key: k, parameter_value: v.to_s } }
|
149
|
+
end
|
150
|
+
|
151
|
+
def load_ami_id
|
152
|
+
ami_data = ssm_client.get_parameter(
|
153
|
+
name: '/aws/service/ecs/optimized-ami/amazon-linux-2/recommended'
|
154
|
+
).to_h[:parameter]
|
155
|
+
|
156
|
+
ami_details = JSON.parse(ami_data[:value]).with_indifferent_access
|
157
|
+
|
158
|
+
ami_details[:image_id]
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|