ufo 3.5.7 → 4.0.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/CHANGELOG.md +24 -0
- data/Gemfile.lock +16 -10
- data/README.md +12 -13
- data/docs/_config.yml +1 -1
- data/docs/_docs/auto-completion.md +4 -4
- data/docs/_docs/automated-cleanup.md +1 -1
- data/docs/_docs/conventions.md +7 -7
- data/docs/_docs/customize-cloudformation.md +36 -0
- data/docs/_docs/faq.md +9 -7
- data/docs/_docs/fargate.md +102 -0
- data/docs/_docs/helpers.md +3 -3
- data/docs/_docs/load-balancer.md +72 -0
- data/docs/_docs/migrations.md +2 -2
- data/docs/_docs/next-steps.md +2 -2
- data/docs/_docs/params.md +12 -41
- data/docs/_docs/route53-support.md +28 -0
- data/docs/_docs/run-in-pieces.md +2 -2
- data/docs/_docs/security-groups.md +54 -0
- data/docs/_docs/settings-cfn.md +11 -0
- data/docs/_docs/settings-network.md +34 -0
- data/docs/_docs/settings.md +18 -15
- data/docs/_docs/single-task.md +3 -3
- data/docs/_docs/ssl-support.md +42 -0
- data/docs/_docs/structure.md +5 -1
- data/docs/_docs/stuck-cloudformation.md +30 -0
- data/docs/_docs/tutorial-ufo-docker-build.md +19 -31
- data/docs/_docs/tutorial-ufo-init.md +16 -12
- data/docs/_docs/tutorial-ufo-ship.md +50 -54
- data/docs/_docs/tutorial-ufo-ships.md +9 -7
- data/docs/_docs/tutorial-ufo-tasks-build.md +26 -17
- data/docs/_docs/ufo-current.md +50 -0
- data/docs/_docs/ufo-env-extra.md +21 -0
- data/docs/_docs/ufo-env.md +6 -13
- data/docs/_docs/ufo-tasks-register.md +3 -3
- data/docs/_docs/upgrade4.md +49 -0
- data/docs/_docs/variables.md +5 -5
- data/docs/_docs/why-cloudformation.md +22 -0
- data/docs/_includes/about.html +1 -1
- data/docs/_includes/cfn-customize.md +39 -0
- data/docs/_includes/commands.html +6 -6
- data/docs/_includes/css/ufo.css +1 -0
- data/docs/_includes/example.html +13 -13
- data/docs/_includes/reference.md +1 -1
- data/docs/_includes/subnav.html +22 -5
- data/docs/_includes/ufo-ship-options.md +7 -6
- data/docs/_reference/ufo-apps.md +36 -0
- data/docs/_reference/ufo-cancel.md +24 -0
- data/docs/_reference/ufo-completion.md +1 -1
- data/docs/_reference/ufo-completion_script.md +1 -1
- data/docs/_reference/ufo-current.md +93 -0
- data/docs/_reference/ufo-deploy.md +18 -17
- data/docs/_reference/ufo-destroy.md +6 -4
- data/docs/_reference/ufo-docker-base.md +7 -7
- data/docs/_reference/ufo-docker-build.md +9 -9
- data/docs/_reference/ufo-docker-clean.md +8 -8
- data/docs/_reference/ufo-docker-name.md +4 -4
- data/docs/_reference/ufo-docker.md +4 -2
- data/docs/_reference/ufo-init.md +31 -20
- data/docs/_reference/ufo-network-help.md +15 -0
- data/docs/_reference/ufo-network-init.md +38 -0
- data/docs/_reference/ufo-network.md +26 -0
- data/docs/_reference/ufo-ps.md +53 -0
- data/docs/_reference/ufo-releases.md +40 -0
- data/docs/_reference/ufo-resources.md +44 -0
- data/docs/_reference/ufo-rollback.md +59 -0
- data/docs/_reference/ufo-scale.md +23 -3
- data/docs/_reference/ufo-ship.md +54 -27
- data/docs/_reference/ufo-ships.md +17 -26
- data/docs/_reference/ufo-stop.md +31 -0
- data/docs/_reference/ufo-task.md +15 -16
- data/docs/_reference/ufo-tasks-build.md +10 -10
- data/docs/_reference/ufo-tasks-register.md +3 -3
- data/docs/_reference/ufo-tasks.md +1 -1
- data/docs/_reference/ufo-upgrade-help.md +15 -0
- data/docs/_reference/ufo-upgrade-v2to3.md +15 -0
- data/docs/_reference/ufo-upgrade-v3_3to3_4.md +15 -0
- data/docs/_reference/ufo-upgrade-v3to4.md +27 -0
- data/docs/_reference/ufo-upgrade.md +28 -0
- data/docs/_reference/ufo-version.md +1 -1
- data/docs/articles.md +2 -2
- data/docs/docs.md +1 -1
- data/docs/img/docs/cloudformation-resources.png +0 -0
- data/docs/img/tutorials/ecs-console-task-definitions.png +0 -0
- data/docs/img/tutorials/ecs-console-ufo-ship.png +0 -0
- data/docs/img/tutorials/ecs-console-ufo-ships.png +0 -0
- data/docs/quick-start.md +21 -9
- data/docs/reference.md +10 -2
- data/exe/ufo +1 -1
- data/lib/cfn/stack.yml +259 -0
- data/lib/template/.ufo/params.yml.tt +21 -60
- data/lib/template/.ufo/settings.yml.tt +6 -1
- data/lib/template/.ufo/settings/cfn/default.yml.tt +55 -0
- data/lib/template/.ufo/settings/network/default.yml.tt +18 -0
- data/lib/template/.ufo/task_definitions.rb.tt +7 -6
- data/lib/template/.ufo/templates/fargate.json.erb +1 -1
- data/lib/template/.ufo/templates/main.json.erb +1 -0
- data/lib/template/.ufo/variables/base.rb.tt +5 -2
- data/lib/template/Dockerfile +10 -15
- data/lib/template/bin/deploy.tt +2 -2
- data/lib/ufo.rb +29 -20
- data/lib/ufo/apps.rb +49 -0
- data/lib/ufo/apps/cfn_map.rb +70 -0
- data/lib/ufo/apps/service.rb +56 -0
- data/lib/ufo/aws_service.rb +15 -6
- data/lib/ufo/base.rb +32 -0
- data/lib/ufo/cancel.rb +23 -0
- data/lib/ufo/cli.rb +91 -27
- data/lib/ufo/core.rb +35 -3
- data/lib/ufo/current.rb +104 -0
- data/lib/ufo/destroy.rb +10 -41
- data/lib/ufo/docker/builder.rb +5 -4
- data/lib/ufo/docker/cleaner.rb +1 -1
- data/lib/ufo/docker/pusher.rb +2 -2
- data/lib/ufo/ecr/cleaner.rb +1 -1
- data/lib/ufo/help/apps.md +12 -0
- data/lib/ufo/help/balancer.md +3 -0
- data/lib/ufo/help/current.md +65 -0
- data/lib/ufo/help/deploy.md +4 -4
- data/lib/ufo/help/destroy.md +3 -3
- data/lib/ufo/help/docker.md +3 -1
- data/lib/ufo/help/docker/base.md +7 -7
- data/lib/ufo/help/docker/build.md +9 -9
- data/lib/ufo/help/docker/clean.md +8 -8
- data/lib/ufo/help/docker/name.md +4 -4
- data/lib/ufo/help/help.md +5 -0
- data/lib/ufo/help/init.md +24 -16
- data/lib/ufo/help/network/init.md +13 -0
- data/lib/ufo/help/ps.md +27 -0
- data/lib/ufo/help/releases.md +16 -0
- data/lib/ufo/help/resources.md +20 -0
- data/lib/ufo/help/rollback.md +35 -0
- data/lib/ufo/help/scale.md +22 -2
- data/lib/ufo/help/ship.md +40 -14
- data/lib/ufo/help/ships.md +4 -13
- data/lib/ufo/help/stop.md +7 -0
- data/lib/ufo/help/task.md +9 -9
- data/lib/ufo/help/tasks/build.md +10 -10
- data/lib/ufo/help/tasks/register.md +3 -3
- data/lib/ufo/help/upgrade/v3to4.md +3 -0
- data/lib/ufo/info.rb +62 -0
- data/lib/ufo/init.rb +36 -23
- data/lib/ufo/log_group.rb +2 -1
- data/lib/ufo/network.rb +24 -0
- data/lib/ufo/network/fetch.rb +41 -0
- data/lib/ufo/network/helper.rb +23 -0
- data/lib/ufo/network/init.rb +26 -0
- data/lib/ufo/param.rb +5 -5
- data/lib/ufo/ps.rb +102 -0
- data/lib/ufo/ps/task.rb +78 -0
- data/lib/ufo/releases.rb +14 -0
- data/lib/ufo/rollback.rb +53 -0
- data/lib/ufo/scale.rb +6 -12
- data/lib/ufo/sequence.rb +7 -0
- data/lib/ufo/setting.rb +7 -6
- data/lib/ufo/setting/profile.rb +24 -0
- data/lib/ufo/ship.rb +35 -326
- data/lib/ufo/stack.rb +203 -0
- data/lib/ufo/stack/context.rb +242 -0
- data/lib/ufo/stack/helper.rb +28 -0
- data/lib/ufo/stack/status.rb +195 -0
- data/lib/ufo/stop.rb +47 -0
- data/lib/ufo/task.rb +96 -15
- data/lib/ufo/tasks/register.rb +1 -1
- data/lib/ufo/template_scope.rb +81 -7
- data/lib/ufo/upgrade.rb +32 -0
- data/lib/ufo/{upgrade3.rb → upgrade/upgrade3.rb} +1 -1
- data/lib/ufo/{upgrade33_to_34.rb → upgrade/upgrade33to34.rb} +2 -2
- data/lib/ufo/upgrade/upgrade4.rb +161 -0
- data/lib/ufo/util.rb +19 -6
- data/lib/ufo/version.rb +1 -1
- data/spec/fixtures/apps/describe_services.json +96 -0
- data/spec/fixtures/cfn/stack-events-complete.json +1080 -0
- data/spec/fixtures/cfn/stack-events-in-progress.json +1080 -0
- data/spec/fixtures/cfn/stack-events-update-rollback-complete.json +1086 -0
- data/spec/fixtures/deployments.json +50 -0
- data/spec/fixtures/ps/describe_tasks.json +58 -0
- data/spec/fixtures/settings.yml +2 -0
- data/spec/lib/apps_spec.rb +20 -0
- data/spec/lib/cli_spec.rb +4 -4
- data/spec/lib/ps_spec.rb +14 -0
- data/spec/lib/setting_spec.rb +2 -1
- data/spec/lib/ship_spec.rb +6 -30
- data/spec/lib/stack/status_spec.rb +76 -0
- data/spec/lib/stop_spec.rb +13 -0
- data/spec/lib/task_spec.rb +5 -2
- data/spec/spec_helper.rb +1 -1
- data/ufo.gemspec +2 -0
- metadata +120 -6
- data/docs/_reference/ufo-upgrade3.md +0 -23
- data/docs/_reference/ufo-upgrade3_3_to_3_4.md +0 -23
data/lib/ufo/stack.rb
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# CloudFormation status codes, full list:
|
|
2
|
+
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-describing-stacks.html
|
|
3
|
+
#
|
|
4
|
+
# CREATE_COMPLETE
|
|
5
|
+
# ROLLBACK_COMPLETE
|
|
6
|
+
# DELETE_COMPLETE
|
|
7
|
+
# UPDATE_COMPLETE
|
|
8
|
+
# UPDATE_ROLLBACK_COMPLETE
|
|
9
|
+
#
|
|
10
|
+
# CREATE_FAILED
|
|
11
|
+
# DELETE_FAILED
|
|
12
|
+
# ROLLBACK_FAILED
|
|
13
|
+
# UPDATE_ROLLBACK_FAILED
|
|
14
|
+
#
|
|
15
|
+
# CREATE_IN_PROGRESS
|
|
16
|
+
# DELETE_IN_PROGRESS
|
|
17
|
+
# REVIEW_IN_PROGRESS
|
|
18
|
+
# ROLLBACK_IN_PROGRESS
|
|
19
|
+
# UPDATE_COMPLETE_CLEANUP_IN_PROGRESS
|
|
20
|
+
# UPDATE_IN_PROGRESS
|
|
21
|
+
# UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS
|
|
22
|
+
# UPDATE_ROLLBACK_IN_PROGRESS
|
|
23
|
+
#
|
|
24
|
+
module Ufo
|
|
25
|
+
class Stack
|
|
26
|
+
autoload :Context, "ufo/stack/context"
|
|
27
|
+
autoload :Helper, "ufo/stack/helper"
|
|
28
|
+
autoload :Status, "ufo/stack/status"
|
|
29
|
+
extend Memoist
|
|
30
|
+
include Helper
|
|
31
|
+
|
|
32
|
+
def initialize(options)
|
|
33
|
+
@options = options
|
|
34
|
+
@task_definition = options[:task_definition]
|
|
35
|
+
@service = options[:service]
|
|
36
|
+
@cluster = @options[:cluster] || default_cluster
|
|
37
|
+
@stack_name = adjust_stack_name(@cluster, options[:service])
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def deploy
|
|
41
|
+
@stack = find_stack(@stack_name)
|
|
42
|
+
if @stack && rollback_complete?(@stack)
|
|
43
|
+
puts "Existing stack in ROLLBACK_COMPLETE state. Deleting stack before continuing."
|
|
44
|
+
cloudformation.delete_stack(stack_name: @stack_name)
|
|
45
|
+
@status.wait
|
|
46
|
+
@status.reset
|
|
47
|
+
@stack = nil # at this point stack has been deleted
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
exit_with_message(@stack) if @stack && !updatable?(@stack)
|
|
51
|
+
|
|
52
|
+
@stack ? perform(:update) : perform(:create)
|
|
53
|
+
|
|
54
|
+
stop_old_tasks if @options[:stop_old_task]
|
|
55
|
+
|
|
56
|
+
return unless @options[:wait]
|
|
57
|
+
status.wait
|
|
58
|
+
|
|
59
|
+
puts status.rollback_error_message if status.update_rollback?
|
|
60
|
+
|
|
61
|
+
status.success?
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def perform(action)
|
|
65
|
+
puts "#{action[0..-2].capitalize}ing stack #{@stack_name.colorize(:green)}..."
|
|
66
|
+
# Example: cloudformation.send("update_stack", stack_options)
|
|
67
|
+
cloudformation.send("#{action}_stack", stack_options)
|
|
68
|
+
rescue Aws::CloudFormation::Errors::ValidationError => e
|
|
69
|
+
handle_stack_error(e)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# do not memoize template_body it can change for a rename retry
|
|
73
|
+
def template_body
|
|
74
|
+
custom_template = "#{Ufo.root}/.ufo/settings/cfn/stack.yml"
|
|
75
|
+
path = if File.exist?(custom_template)
|
|
76
|
+
custom_template
|
|
77
|
+
else
|
|
78
|
+
# built-in default
|
|
79
|
+
File.expand_path("../cfn/stack.yml", File.dirname(__FILE__))
|
|
80
|
+
end
|
|
81
|
+
RenderMePretty.result(path, context: context.scope)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def stack_options
|
|
85
|
+
save_template
|
|
86
|
+
if ENV['SAVE_TEMPLATE_EXIT']
|
|
87
|
+
puts "Template saved. Exiting."
|
|
88
|
+
exit 1
|
|
89
|
+
end
|
|
90
|
+
{
|
|
91
|
+
parameters: parameters,
|
|
92
|
+
stack_name: @stack_name,
|
|
93
|
+
template_body: template_body,
|
|
94
|
+
}
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def parameters
|
|
98
|
+
create_elb, elb_target_group = context.elb_options
|
|
99
|
+
|
|
100
|
+
network = Setting::Profile.new(:network, settings[:network_profile]).data
|
|
101
|
+
# pp network
|
|
102
|
+
elb_subnets = network[:elb_subnets] && !network[:elb_subnets].empty? ?
|
|
103
|
+
network[:elb_subnets] :
|
|
104
|
+
network[:ecs_subnets]
|
|
105
|
+
|
|
106
|
+
hash = {
|
|
107
|
+
Vpc: network[:vpc],
|
|
108
|
+
ElbSubnets: elb_subnets.join(','),
|
|
109
|
+
EcsSubnets: network[:ecs_subnets].join(','),
|
|
110
|
+
|
|
111
|
+
CreateElb: create_elb,
|
|
112
|
+
ElbTargetGroup: elb_target_group,
|
|
113
|
+
ElbEipIds: context.elb_eip_ids,
|
|
114
|
+
|
|
115
|
+
EcsDesiredCount: current_desired_count,
|
|
116
|
+
EcsTaskDefinition: task_definition_arn,
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
hash[:EcsSecurityGroups] = network[:ecs_security_groups].join(',') if network[:ecs_security_groups]
|
|
120
|
+
hash[:ElbSecurityGroups] = network[:elb_security_groups].join(',') if network[:elb_security_groups]
|
|
121
|
+
|
|
122
|
+
hash.map do |k,v|
|
|
123
|
+
if v == :use_previous_value
|
|
124
|
+
{ parameter_key: k, use_previous_value: true }
|
|
125
|
+
else
|
|
126
|
+
{ parameter_key: k, parameter_value: v }
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
memoize :parameters
|
|
131
|
+
|
|
132
|
+
def context
|
|
133
|
+
Context.new(@options.merge(
|
|
134
|
+
cluster: @cluster,
|
|
135
|
+
stack_name: @stack_name,
|
|
136
|
+
stack: @stack,
|
|
137
|
+
))
|
|
138
|
+
end
|
|
139
|
+
memoize :context
|
|
140
|
+
|
|
141
|
+
def current_desired_count
|
|
142
|
+
info = Info.new(@service, @options)
|
|
143
|
+
service = info.service
|
|
144
|
+
if service
|
|
145
|
+
service.desired_count.to_s
|
|
146
|
+
else
|
|
147
|
+
"1" # new service default
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def task_definition_arn
|
|
152
|
+
resp = ecs.describe_task_definition(task_definition: @task_definition)
|
|
153
|
+
resp.task_definition.task_definition_arn
|
|
154
|
+
end
|
|
155
|
+
memoize :task_definition_arn
|
|
156
|
+
|
|
157
|
+
# Store template in tmp in case for debugging
|
|
158
|
+
def save_template
|
|
159
|
+
path = "/tmp/ufo/#{@stack_name}/stack.yml"
|
|
160
|
+
FileUtils.mkdir_p(File.dirname(path))
|
|
161
|
+
IO.write(path, template_body)
|
|
162
|
+
puts "Generated template saved at: #{path}"
|
|
163
|
+
|
|
164
|
+
path = "/tmp/ufo/#{@stack_name}/parameters.yml"
|
|
165
|
+
IO.write(path, JSON.pretty_generate(parameters))
|
|
166
|
+
puts "Generated parameters saved at: #{path}"
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def exit_with_message(stack)
|
|
170
|
+
region = `aws configure get region`.strip rescue "us-east-1"
|
|
171
|
+
url = "https://console.aws.amazon.com/cloudformation/home?region=#{region}#/stacks"
|
|
172
|
+
puts "The stack is not in an updateable state: #{stack.stack_status.colorize(:yellow)}."
|
|
173
|
+
puts "Here's the CloudFormation url to check for more details #{url}"
|
|
174
|
+
exit 1
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# Assume only first container_definition to get the container info.
|
|
178
|
+
# Stack:arn:aws:cloudformation:... is in ROLLBACK_COMPLETE state and can not be updated.
|
|
179
|
+
def handle_stack_error(e)
|
|
180
|
+
case e.message
|
|
181
|
+
when /is in ROLLBACK_COMPLETE state and can not be updated/
|
|
182
|
+
puts "The #{@stack_name} stack is in #{"ROLLBACK_COMPLETE".colorize(:red)} and cannot be updated. Deleted the stack and try again."
|
|
183
|
+
region = `aws configure get region`.strip rescue 'us-east-1'
|
|
184
|
+
url = "https://console.aws.amazon.com/cloudformation/home?region=#{region}"
|
|
185
|
+
puts "Here's the CloudFormation console url: #{url}"
|
|
186
|
+
exit 1
|
|
187
|
+
when /No updates are to be performed/
|
|
188
|
+
puts "There are no updates to be performed. Exiting.".colorize(:yellow)
|
|
189
|
+
exit 1
|
|
190
|
+
else
|
|
191
|
+
raise
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def rollback_complete?(stack)
|
|
196
|
+
stack.stack_status == 'ROLLBACK_COMPLETE'
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def updatable?(stack)
|
|
200
|
+
stack.stack_status =~ /_COMPLETE$/
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
end
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
class Ufo::Stack
|
|
2
|
+
class Context
|
|
3
|
+
extend Memoist
|
|
4
|
+
include Helper
|
|
5
|
+
|
|
6
|
+
def initialize(options)
|
|
7
|
+
@options = options
|
|
8
|
+
@task_definition = options[:task_definition]
|
|
9
|
+
@service = options[:service]
|
|
10
|
+
# no need to adjust @cluster or @stack_name because it was adjusted in Stack#initialize
|
|
11
|
+
@cluster = options[:cluster]
|
|
12
|
+
@stack_name = options[:stack_name]
|
|
13
|
+
|
|
14
|
+
@stack = options[:stack]
|
|
15
|
+
@new_stack = !@stack
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def scope
|
|
19
|
+
scope = Ufo::TemplateScope.new(Ufo::DSL::Helper.new, nil)
|
|
20
|
+
# Add additional variable to scope for CloudFormation template.
|
|
21
|
+
# Dirties the scope but needed.
|
|
22
|
+
vars = {
|
|
23
|
+
cluster: @cluster,
|
|
24
|
+
stack_name: @stack_name, # used in custom_properties
|
|
25
|
+
pretty_service_name: Ufo.pretty_service_name(@service),
|
|
26
|
+
container: container,
|
|
27
|
+
# elb options remember that their 'state'
|
|
28
|
+
create_elb: create_elb?, # helps set Ecs DependsOn
|
|
29
|
+
elb_type: elb_type,
|
|
30
|
+
subnet_mappings: subnet_mappings,
|
|
31
|
+
create_route53: create_elb? && cfn[:dns] && cfn[:dns][:name],
|
|
32
|
+
default_target_group_protocol: default_target_group_protocol,
|
|
33
|
+
default_listener_protocol: default_listener_protocol,
|
|
34
|
+
}
|
|
35
|
+
# puts "vars:".colorize(:cyan)
|
|
36
|
+
# pp vars
|
|
37
|
+
scope.assign_instance_variables(vars)
|
|
38
|
+
scope
|
|
39
|
+
end
|
|
40
|
+
memoize :scope
|
|
41
|
+
|
|
42
|
+
def default_target_group_protocol
|
|
43
|
+
return 'TCP' if elb_type == 'network'
|
|
44
|
+
'HTTP'
|
|
45
|
+
# cfn[:target_group][:port] == 443 ? 'HTTPS' : 'HTTP'
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def default_listener_protocol
|
|
49
|
+
return 'TCP' if elb_type == 'network'
|
|
50
|
+
'HTTP'
|
|
51
|
+
# cfn[:listener][:port] == 443 ? 'HTTPS' : 'HTTP'
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def container
|
|
55
|
+
resp = ecs.describe_task_definition(task_definition: @task_definition)
|
|
56
|
+
task_definition = resp.task_definition
|
|
57
|
+
|
|
58
|
+
container_def = task_definition["container_definitions"].first
|
|
59
|
+
requires_compatibilities = task_definition["requires_compatibilities"]
|
|
60
|
+
fargate = requires_compatibilities && requires_compatibilities == ["FARGATE"]
|
|
61
|
+
network_mode = task_definition["network_mode"]
|
|
62
|
+
|
|
63
|
+
mappings = container_def["port_mappings"] || []
|
|
64
|
+
unless mappings.empty?
|
|
65
|
+
port = mappings.first["container_port"]
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
result = {
|
|
69
|
+
name: container_def["name"],
|
|
70
|
+
fargate: fargate,
|
|
71
|
+
network_mode: network_mode, # awsvpc, bridge, etc
|
|
72
|
+
}
|
|
73
|
+
result[:port] = port if port
|
|
74
|
+
result
|
|
75
|
+
end
|
|
76
|
+
memoize :container
|
|
77
|
+
|
|
78
|
+
def create_elb?
|
|
79
|
+
create_elb, _ = elb_options
|
|
80
|
+
create_elb == "true" # convert to boolean
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# if --elb is not set at all, so it's nil. Thhen it defaults to creating the
|
|
84
|
+
# load balancer if the ecs service has a container name "web".
|
|
85
|
+
#
|
|
86
|
+
# --elb '' - wont crete an elb
|
|
87
|
+
# --elb 'auto' - creates an elb
|
|
88
|
+
# --elb arn:... - wont create elb and use the existing target group
|
|
89
|
+
#
|
|
90
|
+
def elb_options
|
|
91
|
+
case @options[:elb]
|
|
92
|
+
when "auto", "true", "yes"
|
|
93
|
+
create_elb = "true"
|
|
94
|
+
elb_target_group = ""
|
|
95
|
+
when "false", "0", "no"
|
|
96
|
+
create_elb = "false"
|
|
97
|
+
elb_target_group = ""
|
|
98
|
+
when /^arn:aws:elasticloadbalancing.*targetgroup/
|
|
99
|
+
create_elb = "false"
|
|
100
|
+
elb_target_group = @options[:elb]
|
|
101
|
+
when "", nil
|
|
102
|
+
create_elb, elb_target_group = default_elb_options
|
|
103
|
+
else
|
|
104
|
+
puts "Invalid --elb option provided: #{@options[:elb].inspect}".colorize(:red)
|
|
105
|
+
puts "Exiting."
|
|
106
|
+
exit 1
|
|
107
|
+
end
|
|
108
|
+
[create_elb, elb_target_group]
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def default_elb_options
|
|
112
|
+
# cannot use :use_previous_value because need to know the create_elb value to
|
|
113
|
+
# compile a template with the right DependsOn for the Ecs service
|
|
114
|
+
unless @new_stack
|
|
115
|
+
create_elb = get_parameter_value(@stack, "CreateElb")
|
|
116
|
+
elb_target_group = get_parameter_value(@stack, "ElbTargetGroup")
|
|
117
|
+
return [create_elb, elb_target_group]
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# default is to create the load balancer is if container name is web
|
|
121
|
+
# and no --elb option is provided
|
|
122
|
+
create_elb = container[:name] == "web" ? "true" : "false"
|
|
123
|
+
elb_target_group = ""
|
|
124
|
+
[create_elb, elb_target_group]
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def get_parameter_value(stack, key)
|
|
128
|
+
param = stack.parameters.find do |p|
|
|
129
|
+
p.parameter_key == key
|
|
130
|
+
end
|
|
131
|
+
param.parameter_value
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def reset_empty_eip_ids?
|
|
135
|
+
# reset and remove eip allocation ids check
|
|
136
|
+
@options[:elb_eip_ids] && @options[:elb_eip_ids].detect { |x| [' ', 'empty'].include?(x) }
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def subnet_mappings
|
|
140
|
+
return [] if reset_empty_eip_ids?
|
|
141
|
+
|
|
142
|
+
elb_eip_ids = normalize_elb_eip_ids
|
|
143
|
+
return build_subnet_mappings!(elb_eip_ids) unless elb_eip_ids.empty?
|
|
144
|
+
|
|
145
|
+
unless @new_stack
|
|
146
|
+
elb_eip_ids = get_parameter_value(@stack, "ElbEipIds").split(',')
|
|
147
|
+
build_subnet_mappings(elb_eip_ids)
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def normalize_elb_eip_ids
|
|
152
|
+
elb_eip_ids = @options[:elb_eip_ids] || []
|
|
153
|
+
elb_eip_ids.uniq!
|
|
154
|
+
elb_eip_ids
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Returns string, used as CloudFormation parameter.
|
|
158
|
+
def elb_eip_ids
|
|
159
|
+
return '' if reset_empty_eip_ids?
|
|
160
|
+
|
|
161
|
+
elb_eip_ids = normalize_elb_eip_ids
|
|
162
|
+
return elb_eip_ids.join(',') unless elb_eip_ids.empty?
|
|
163
|
+
|
|
164
|
+
unless @new_stack
|
|
165
|
+
return get_parameter_value(@stack, "ElbEipIds")
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
''
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def build_subnet_mappings!(allocations)
|
|
172
|
+
unless allocations.size == network[:elb_subnets].size
|
|
173
|
+
# puts "caller:".colorize(:cyan)
|
|
174
|
+
# puts caller
|
|
175
|
+
puts "ERROR: The allocation_ids must match in length to the subnets.".colorize(:red)
|
|
176
|
+
puts "Please double check that .ufo/settings/network/#{settings[:network_profile]} has the same number of subnets as the eip allocation ids are you specifying."
|
|
177
|
+
subnets = network[:elb_subnets]
|
|
178
|
+
puts "Conigured subnets: #{subnets.inspect}"
|
|
179
|
+
puts "Specified allocation ids: #{allocations.inspect}"
|
|
180
|
+
exit 1
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
build_subnet_mappings(allocations)
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def build_subnet_mappings(allocations)
|
|
187
|
+
mappings = []
|
|
188
|
+
allocations.sort.each_with_index do |allocation_id, i|
|
|
189
|
+
mappings << [allocation_id, network[:elb_subnets][i]]
|
|
190
|
+
end
|
|
191
|
+
mappings
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def elb_type
|
|
195
|
+
# if option explicitly specified then change the elb type
|
|
196
|
+
return @options[:elb_type] if @options[:elb_type]
|
|
197
|
+
# user is trying to create a network load balancer if --elb-eip-ids is used
|
|
198
|
+
elb_eip_ids = normalize_elb_eip_ids
|
|
199
|
+
if !elb_eip_ids.empty?
|
|
200
|
+
return "network"
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# if not explicitly set, new stack will defeault to application load balancer
|
|
204
|
+
if @new_stack # default for new stack
|
|
205
|
+
return "application"
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# find existing load balancer for type
|
|
209
|
+
resp = cloudformation.describe_stack_resources(stack_name: @stack_name)
|
|
210
|
+
resources = resp.stack_resources
|
|
211
|
+
elb_resource = resources.find do |resource|
|
|
212
|
+
resource.logical_resource_id == "Elb"
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
# In the case when stack exists and there is no elb resource, the elb type
|
|
216
|
+
# doesnt really matter because the elb wont be created since the CreateElb
|
|
217
|
+
# parameter is set to false. The elb type only needs to be set for the
|
|
218
|
+
# template to validate.
|
|
219
|
+
return "application" unless elb_resource
|
|
220
|
+
|
|
221
|
+
resp = elb.describe_load_balancers(load_balancer_arns: [elb_resource.physical_resource_id])
|
|
222
|
+
load_balancer = resp.load_balancers.first
|
|
223
|
+
load_balancer.type
|
|
224
|
+
end
|
|
225
|
+
memoize :elb_type
|
|
226
|
+
|
|
227
|
+
def network
|
|
228
|
+
Ufo::Setting::Profile.new(:network, settings[:network_profile]).data
|
|
229
|
+
end
|
|
230
|
+
memoize :network
|
|
231
|
+
|
|
232
|
+
def cfn
|
|
233
|
+
Ufo::Setting::Profile.new(:cfn, settings[:cfn_profile]).data
|
|
234
|
+
end
|
|
235
|
+
memoize :cfn
|
|
236
|
+
|
|
237
|
+
def settings
|
|
238
|
+
Ufo.settings
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
end
|
|
242
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
class Ufo::Stack
|
|
2
|
+
module Helper
|
|
3
|
+
include Ufo::AwsService
|
|
4
|
+
include Ufo::Util
|
|
5
|
+
extend Memoist
|
|
6
|
+
|
|
7
|
+
def find_stack(stack_name)
|
|
8
|
+
resp = cloudformation.describe_stacks(stack_name: stack_name)
|
|
9
|
+
resp.stacks.first
|
|
10
|
+
rescue Aws::CloudFormation::Errors::ValidationError => e
|
|
11
|
+
# example: Stack with id demo-web does not exist
|
|
12
|
+
if e.message =~ /Stack with/ && e.message =~ /does not exist/
|
|
13
|
+
nil
|
|
14
|
+
else
|
|
15
|
+
raise
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def adjust_stack_name(cluster, service)
|
|
20
|
+
[cluster, Ufo.pretty_service_name(service)].compact.join('-')
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def status
|
|
24
|
+
Status.new(@stack_name)
|
|
25
|
+
end
|
|
26
|
+
memoize :status
|
|
27
|
+
end
|
|
28
|
+
end
|