ufo 3.5.7 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -0,0 +1,195 @@
|
|
1
|
+
require 'time'
|
2
|
+
|
3
|
+
# CloudFormation status codes, full list:
|
4
|
+
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-describing-stacks.html
|
5
|
+
#
|
6
|
+
# CREATE_COMPLETE
|
7
|
+
# ROLLBACK_COMPLETE
|
8
|
+
# DELETE_COMPLETE
|
9
|
+
# UPDATE_COMPLETE
|
10
|
+
# UPDATE_ROLLBACK_COMPLETE
|
11
|
+
#
|
12
|
+
# CREATE_FAILED
|
13
|
+
# DELETE_FAILED
|
14
|
+
# ROLLBACK_FAILED
|
15
|
+
# UPDATE_ROLLBACK_FAILED
|
16
|
+
#
|
17
|
+
# CREATE_IN_PROGRESS
|
18
|
+
# DELETE_IN_PROGRESS
|
19
|
+
# REVIEW_IN_PROGRESS
|
20
|
+
# ROLLBACK_IN_PROGRESS
|
21
|
+
# UPDATE_COMPLETE_CLEANUP_IN_PROGRESS
|
22
|
+
# UPDATE_IN_PROGRESS
|
23
|
+
# UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS
|
24
|
+
# UPDATE_ROLLBACK_IN_PROGRESS
|
25
|
+
#
|
26
|
+
class Ufo::Stack
|
27
|
+
class Status
|
28
|
+
include Ufo::AwsService
|
29
|
+
include Ufo::Util
|
30
|
+
|
31
|
+
attr_reader :events
|
32
|
+
def initialize(stack_name)
|
33
|
+
@stack_name = stack_name
|
34
|
+
reset
|
35
|
+
end
|
36
|
+
|
37
|
+
def reset
|
38
|
+
@events = [] # constantly replaced with recent events
|
39
|
+
@last_shown_event_id = nil
|
40
|
+
@stack_deletion_completed = nil
|
41
|
+
end
|
42
|
+
|
43
|
+
# check for /(_COMPLETE|_FAILED)$/ status
|
44
|
+
def wait
|
45
|
+
start_time = Time.now
|
46
|
+
|
47
|
+
refresh_events
|
48
|
+
until completed || @stack_deletion_completed
|
49
|
+
show_events
|
50
|
+
end
|
51
|
+
show_events(true) # show the final event
|
52
|
+
|
53
|
+
if @stack_deletion_completed
|
54
|
+
puts "Stack #{@stack_name} deleted."
|
55
|
+
return
|
56
|
+
end
|
57
|
+
|
58
|
+
if last_event_status =~ /_FAILED/
|
59
|
+
puts "Stack failed: #{last_event_status}".colorize(:red)
|
60
|
+
puts "Stack reason #{@events[0]["resource_status_reason"]}".colorize(:red)
|
61
|
+
elsif last_event_status =~ /_ROLLBACK_/
|
62
|
+
puts "Stack rolled back: #{last_event_status}".colorize(:red)
|
63
|
+
else # success
|
64
|
+
puts "Stack success status: #{last_event_status}".colorize(:green)
|
65
|
+
end
|
66
|
+
|
67
|
+
took = Time.now - start_time
|
68
|
+
puts "Time took for stack deployment: #{pretty_time(took).green}."
|
69
|
+
end
|
70
|
+
|
71
|
+
def completed
|
72
|
+
last_event_status =~ /(_COMPLETE|_FAILED)$/ &&
|
73
|
+
@events[0]["resource_type"] == "AWS::CloudFormation::Stack"
|
74
|
+
end
|
75
|
+
|
76
|
+
def last_event_status
|
77
|
+
@events[0]["resource_status"]
|
78
|
+
end
|
79
|
+
|
80
|
+
# Only shows new events
|
81
|
+
def show_events(final=false)
|
82
|
+
if @last_shown_event_id.nil?
|
83
|
+
i = find_index(:start)
|
84
|
+
print_events(i)
|
85
|
+
else
|
86
|
+
i = find_index(:last_shown)
|
87
|
+
# puts "last_shown index #{i}"
|
88
|
+
print_events(i-1) unless i == 0
|
89
|
+
end
|
90
|
+
|
91
|
+
return if final
|
92
|
+
sleep 5 unless ENV['TEST']
|
93
|
+
refresh_events
|
94
|
+
end
|
95
|
+
|
96
|
+
def print_events(i)
|
97
|
+
@events[0..i].reverse.each do |e|
|
98
|
+
print_event(e)
|
99
|
+
end
|
100
|
+
@last_shown_event_id = @events[0]["event_id"]
|
101
|
+
# puts "@last_shown_event_id #{@last_shown_event_id.inspect}"
|
102
|
+
end
|
103
|
+
|
104
|
+
def print_event(e)
|
105
|
+
message = [
|
106
|
+
event_time(e["timestamp"]),
|
107
|
+
e["resource_status"],
|
108
|
+
e["resource_type"],
|
109
|
+
e["logical_resource_id"],
|
110
|
+
e["resource_status_reason"]
|
111
|
+
].join(" ")
|
112
|
+
message = message.colorize(:red) if e["resource_status"] =~ /_FAILED/
|
113
|
+
puts message
|
114
|
+
end
|
115
|
+
|
116
|
+
# https://stackoverflow.com/questions/18000432/rails-12-hour-am-pm-range-for-a-day
|
117
|
+
def event_time(timestamp)
|
118
|
+
Time.parse(timestamp.to_s).localtime.strftime("%I:%M:%S%p")
|
119
|
+
end
|
120
|
+
|
121
|
+
# refreshes the loaded events in memory
|
122
|
+
def refresh_events
|
123
|
+
resp = cloudformation.describe_stack_events(stack_name: @stack_name)
|
124
|
+
@events = resp["stack_events"]
|
125
|
+
rescue Aws::CloudFormation::Errors::ValidationError => e
|
126
|
+
if e.message =~ /Stack .* does not exis/
|
127
|
+
@stack_deletion_completed = true
|
128
|
+
else
|
129
|
+
raise
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def find_index(name)
|
134
|
+
send("#{name}_index")
|
135
|
+
end
|
136
|
+
|
137
|
+
def start_index
|
138
|
+
@events.find_index do |event|
|
139
|
+
event["resource_type"] == "AWS::CloudFormation::Stack" &&
|
140
|
+
event["resource_status_reason"] == "User Initiated"
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def last_shown_index
|
145
|
+
@events.find_index do |event|
|
146
|
+
event["event_id"] == @last_shown_event_id
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def success?
|
151
|
+
resource_status = @events[0]["resource_status"]
|
152
|
+
%w[CREATE_COMPLETE UPDATE_COMPLETE].include?(resource_status)
|
153
|
+
end
|
154
|
+
|
155
|
+
def update_rollback?
|
156
|
+
@events[0]["resource_status"] == "UPDATE_ROLLBACK_COMPLETE"
|
157
|
+
end
|
158
|
+
|
159
|
+
def find_update_failed_event
|
160
|
+
i = @events.find_index do |event|
|
161
|
+
event["resource_type"] == "AWS::CloudFormation::Stack" &&
|
162
|
+
event["resource_status_reason"] == "User Initiated"
|
163
|
+
end
|
164
|
+
|
165
|
+
@events[0..i].reverse.find do |e|
|
166
|
+
e["resource_status"] == "UPDATE_FAILED"
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def rollback_error_message
|
171
|
+
return unless update_rollback?
|
172
|
+
|
173
|
+
event = find_update_failed_event
|
174
|
+
return unless event
|
175
|
+
|
176
|
+
reason = event["resource_status_reason"]
|
177
|
+
messages_map.each do |pattern, message|
|
178
|
+
if reason =~ pattern
|
179
|
+
return message
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
reason # default message is original reason if not found in messages map
|
184
|
+
end
|
185
|
+
|
186
|
+
def messages_map
|
187
|
+
{
|
188
|
+
/CloudFormation cannot update a stack when a custom-named resource requires replacing/ => "A workaround is to run ufo again with STATIC_NAME=0 and to switch to dynamic names for resources. Then run ufo again with STATIC_NAME=1 to get back to statically name resources. Note, there are caveats with the workaround.",
|
189
|
+
/cannot be associated with more than one load balancer/ => "There's was an issue updating the stack. Target groups can only be associated with one load balancer at a time. The workaround for this is to use UFO_FORCE_TARGET_GROUP=1 and run the command again. This will force the recreation of the target group resource.",
|
190
|
+
/SetSubnets is not supported for load balancers of type/ => "Changing subnets for Network Load Balancers is currently not supported. You can try workarouding this with UFO_FORCE_ELB=1 and run the command again. This will force the recreation of the elb resource."
|
191
|
+
}
|
192
|
+
end
|
193
|
+
|
194
|
+
end
|
195
|
+
end
|
data/lib/ufo/stop.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
module Ufo
|
2
|
+
class Stop < Base
|
3
|
+
def run
|
4
|
+
info = Info.new(@service, @options)
|
5
|
+
service = info.service
|
6
|
+
@deployments = service.deployments
|
7
|
+
if @deployments.size > 1
|
8
|
+
stop_old_tasks(service.service_name)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def stop_old_tasks(service_name)
|
13
|
+
# json = JSON.pretty_generate(deployments.map(&:to_h))
|
14
|
+
# IO.write("/tmp/deployments.json", json)
|
15
|
+
tasks = service_tasks(@cluster, service_name)
|
16
|
+
reason = "Ufo #{Ufo::VERSION} has deployed new code and stopping old tasks."
|
17
|
+
tasks.each do |task|
|
18
|
+
next if task["task_definition_arn"] == latest_deployed_arn
|
19
|
+
log "Stopping task #{task["task_arn"]}"
|
20
|
+
ecs.stop_task(cluster: @cluster, task: task["task_arn"], reason: reason)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# latest deployment task definition arn
|
25
|
+
def latest_deployed_arn
|
26
|
+
latest = @deployments.sort_by do |deployment|
|
27
|
+
Time.parse(deployment["created_at"].to_s)
|
28
|
+
end.last
|
29
|
+
latest["task_definition"]
|
30
|
+
end
|
31
|
+
|
32
|
+
def service_tasks(cluster, service_name)
|
33
|
+
all_task_arns = ecs.list_tasks(cluster: cluster, service_name: service_name).task_arns
|
34
|
+
return [] if all_task_arns.empty?
|
35
|
+
ecs.describe_tasks(cluster: cluster, tasks: all_task_arns).tasks
|
36
|
+
end
|
37
|
+
|
38
|
+
def log(text)
|
39
|
+
path = "#{Ufo.root}/.ufo/log/stop.log"
|
40
|
+
FileUtils.mkdir_p(File.dirname(path))
|
41
|
+
File.open(path, 'a') do |f|
|
42
|
+
f.puts("#{Time.now} #{text}")
|
43
|
+
end
|
44
|
+
puts text unless @options[:mute]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/ufo/task.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
|
-
require 'memoist'
|
2
|
-
|
3
1
|
module Ufo
|
4
|
-
class Task
|
2
|
+
class Task < Base
|
5
3
|
extend Memoist
|
6
4
|
|
7
5
|
include Util
|
@@ -21,7 +19,9 @@ module Ufo
|
|
21
19
|
cluster: @cluster,
|
22
20
|
task_definition: @task_definition
|
23
21
|
}
|
24
|
-
task_options = task_options
|
22
|
+
task_options = adjust_fargate_options(task_options)
|
23
|
+
task_options = task_options.merge(user_params[:run_task] || {})
|
24
|
+
task_options = adjust_security_groups(task_options)
|
25
25
|
|
26
26
|
if @options[:command]
|
27
27
|
task_options.merge!(overrides: overrides)
|
@@ -35,6 +35,7 @@ module Ufo
|
|
35
35
|
end
|
36
36
|
|
37
37
|
resp = run_task(task_options)
|
38
|
+
exit_if_failures!(resp)
|
38
39
|
unless @options[:mute]
|
39
40
|
task_arn = resp.tasks[0].task_arn
|
40
41
|
puts "Task ARN: #{task_arn}"
|
@@ -43,6 +44,25 @@ module Ufo
|
|
43
44
|
end
|
44
45
|
end
|
45
46
|
|
47
|
+
# Pretty hard to produce this edge case. Happens when:
|
48
|
+
# launch_type: EC2
|
49
|
+
# network_mode: awsvpc
|
50
|
+
# assign_public_ip: DISABLED
|
51
|
+
def exit_if_failures!(resp)
|
52
|
+
return if resp[:failures].nil? || resp[:failures].empty?
|
53
|
+
|
54
|
+
puts "There was a failure running the ECS task.".colorize(:red)
|
55
|
+
puts "This might be happen if you have a network_mode of awsvpc and have assigned_public_ip to DISABLED."
|
56
|
+
puts "This cryptic error also shows up if the network settings have security groups and subnets that are not in the same vpc as the ECS cluster container instances. Please double check that."
|
57
|
+
puts "You can use this command to quickly reconfigure the network settings:"
|
58
|
+
puts " ufo network init --vpc-id XXX."
|
59
|
+
puts "More details on the can be found under the 'Task Networking Considerations' section at: "
|
60
|
+
puts "https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-networking.html"
|
61
|
+
puts "Original response with failures:"
|
62
|
+
pp resp
|
63
|
+
exit 1
|
64
|
+
end
|
65
|
+
|
46
66
|
def run_task(options)
|
47
67
|
puts "Equivalent aws cli command:"
|
48
68
|
puts " aws ecs run-task --cluster #{@cluster} --task-definition #{options[:task_definition]}".colorize(:green)
|
@@ -55,12 +75,66 @@ module Ufo
|
|
55
75
|
else
|
56
76
|
raise
|
57
77
|
end
|
78
|
+
rescue Aws::ECS::Errors::InvalidParameterException => e
|
79
|
+
if e.message =~ /Network Configuration must be provided when networkMode 'awsvpc' is specified/
|
80
|
+
puts "ERROR: #{e.class} #{e.message}".colorize(:red)
|
81
|
+
puts "Please double check .ufo/params.yml and make sure that network_configuration is set."
|
82
|
+
puts "Or run change the task definition template in .ufo/templates so it does not use vpcmode."
|
83
|
+
exit 1
|
84
|
+
else
|
85
|
+
raise
|
86
|
+
end
|
58
87
|
end
|
59
88
|
|
60
89
|
private
|
90
|
+
# adjust network_configuration based on fargate and network mode of awsvpc
|
91
|
+
def adjust_fargate_options(options)
|
92
|
+
task_def = recent_task_definition
|
93
|
+
return options unless task_def[:network_mode] == "awsvpc"
|
94
|
+
|
95
|
+
awsvpc_conf = { subnets: network[:ecs_subnets] }
|
96
|
+
if task_def[:requires_compatibilities] == ["FARGATE"]
|
97
|
+
awsvpc_conf[:assign_public_ip] = "ENABLED"
|
98
|
+
options[:launch_type] = "FARGATE"
|
99
|
+
end
|
100
|
+
|
101
|
+
options[:network_configuration] = { awsvpc_configuration: awsvpc_conf }
|
102
|
+
options
|
103
|
+
end
|
104
|
+
|
105
|
+
# Ensures at least 1 security group is assigned if awsvpc_configuration
|
106
|
+
# is provided.
|
107
|
+
def adjust_security_groups(options)
|
108
|
+
return options unless options[:network_configuration] &&
|
109
|
+
options[:network_configuration][:awsvpc_configuration]
|
110
|
+
|
111
|
+
awsvpc_conf = options[:network_configuration][:awsvpc_configuration]
|
112
|
+
|
113
|
+
security_groups = awsvpc_conf[:security_groups]
|
114
|
+
if [nil, '', 'nil'].include?(security_groups)
|
115
|
+
security_groups = []
|
116
|
+
end
|
117
|
+
if security_groups.empty?
|
118
|
+
fetch = Network::Fetch.new(network[:vpc])
|
119
|
+
sg = fetch.security_group_id
|
120
|
+
security_groups << sg
|
121
|
+
security_groups.uniq!
|
122
|
+
end
|
123
|
+
|
124
|
+
# override security groups
|
125
|
+
options[:network_configuration][:awsvpc_configuration][:security_groups] = security_groups
|
126
|
+
options
|
127
|
+
end
|
128
|
+
|
129
|
+
def network
|
130
|
+
settings = Ufo.settings
|
131
|
+
Setting::Profile.new(:network, settings[:network_profile]).data
|
132
|
+
end
|
133
|
+
memoize :network
|
134
|
+
|
61
135
|
def cloudwatch_info(task_arn)
|
62
|
-
config =
|
63
|
-
container_name =
|
136
|
+
config = container_definition[:log_configuration]
|
137
|
+
container_name = container_definition[:name]
|
64
138
|
|
65
139
|
return unless config && config[:log_driver] == "awslogs"
|
66
140
|
|
@@ -79,7 +153,6 @@ module Ufo
|
|
79
153
|
# only using the overrides to override the container command
|
80
154
|
def overrides
|
81
155
|
command = @options[:command] # Thor parser ensure this is always an array
|
82
|
-
container_definition = original_container_definition
|
83
156
|
{
|
84
157
|
container_overrides: [
|
85
158
|
{
|
@@ -91,17 +164,25 @@ module Ufo
|
|
91
164
|
}
|
92
165
|
end
|
93
166
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
167
|
+
# Usually most recent task definition.
|
168
|
+
# If user has specified task_definition with specific version like
|
169
|
+
# demo-web:8
|
170
|
+
# Then it'll be that exact task definnition.
|
171
|
+
def recent_task_definition
|
172
|
+
arns = task_definition_arns(@task_definition)
|
99
173
|
# "arn:aws:ecs:us-east-1:<aws_account_id>:task-definition/wordpress:6",
|
100
|
-
last_definition_arn =
|
174
|
+
last_definition_arn = arns.first
|
175
|
+
puts "last_definition_arn #{last_definition_arn}"
|
101
176
|
task_name = last_definition_arn.split("/").last
|
102
177
|
resp = ecs.describe_task_definition(task_definition: task_name)
|
103
|
-
|
178
|
+
|
179
|
+
resp.task_definition
|
180
|
+
end
|
181
|
+
memoize :recent_task_definition
|
182
|
+
|
183
|
+
# container definition from the most recent task definition
|
184
|
+
def container_definition
|
185
|
+
recent_task_definition.container_definitions[0].to_h
|
104
186
|
end
|
105
|
-
memoize :original_container_definition
|
106
187
|
end
|
107
188
|
end
|
data/lib/ufo/tasks/register.rb
CHANGED
@@ -20,7 +20,7 @@ module Ufo
|
|
20
20
|
@options = options
|
21
21
|
end
|
22
22
|
|
23
|
-
# aws ecs register-task-definition --cli-input-json file://.ufo/output/
|
23
|
+
# aws ecs register-task-definition --cli-input-json file://.ufo/output/demo-web-prod.json
|
24
24
|
def register
|
25
25
|
data = JSON.parse(IO.read(@template_definition_path))
|
26
26
|
data = rubyize_format(data)
|
data/lib/ufo/template_scope.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
module Ufo
|
2
2
|
class TemplateScope
|
3
|
+
extend Memoist
|
4
|
+
|
3
5
|
attr_reader :helper
|
4
6
|
attr_reader :task_definition_name
|
5
7
|
def initialize(helper=nil, task_definition_name=nil)
|
@@ -35,14 +37,86 @@ module Ufo
|
|
35
37
|
instance_eval(IO.read(path), path) if File.exist?(path)
|
36
38
|
end
|
37
39
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
40
|
+
# Add additional instance variables to template_scope
|
41
|
+
def assign_instance_variables(vars)
|
42
|
+
vars.each do |k,v|
|
43
|
+
instance_variable_set("@#{k}".to_sym, v)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def network
|
48
|
+
Ufo::Setting::Profile.new(:network, settings[:network_profile]).data
|
49
|
+
end
|
50
|
+
memoize :network
|
51
|
+
|
52
|
+
def cfn
|
53
|
+
Ufo::Setting::Profile.new(:cfn, settings[:cfn_profile]).data
|
54
|
+
end
|
55
|
+
memoize :cfn
|
56
|
+
|
57
|
+
def settings
|
58
|
+
Ufo.settings
|
59
|
+
end
|
60
|
+
|
61
|
+
def custom_properties(resource)
|
62
|
+
resource = resource.to_s.underscore
|
63
|
+
properties = cfn[resource.to_sym]
|
64
|
+
return unless properties
|
65
|
+
|
66
|
+
# transform keys: camelize
|
67
|
+
properties = properties.deep_stringify_keys.deep_transform_keys do |key|
|
68
|
+
if key == key.upcase # trying to generalize special rule for dns.TTL
|
69
|
+
key # leave key alone if key is already in all upcase
|
70
|
+
else
|
71
|
+
key.camelize
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
substitute_variables!(properties)
|
76
|
+
|
77
|
+
yaml = YAML.dump(properties)
|
78
|
+
# add spaces in front on each line
|
79
|
+
yaml.split("\n")[1..-1].map do |line|
|
80
|
+
" #{line}"
|
81
|
+
end.join("\n") + "\n"
|
82
|
+
end
|
83
|
+
|
84
|
+
# Substitute special variables that cannot be baked into the template
|
85
|
+
# because they are dynamically assigned. Only one special variable:
|
86
|
+
#
|
87
|
+
# {stack_name}
|
88
|
+
def substitute_variables!(properties)
|
89
|
+
# transform values and substitute for special values
|
90
|
+
# https://stackoverflow.com/questions/34595142/process-nested-hash-to-convert-all-values-to-strings
|
91
|
+
#
|
92
|
+
# Examples:
|
93
|
+
# "{stack_name}.stag.boltops.com." => development-demo-web.stag.boltops.com.
|
94
|
+
# "{stack_name}.stag.boltops.com." => dev-demo-web-2.stag.boltops.com.
|
95
|
+
properties.deep_merge(properties) do |_,_,v|
|
96
|
+
if v.is_a?(String)
|
97
|
+
v.sub!('{stack_name}', @stack_name) # unsure why need shebang, but it works
|
98
|
+
else
|
99
|
+
v
|
100
|
+
end
|
101
|
+
end
|
102
|
+
properties
|
103
|
+
end
|
104
|
+
|
105
|
+
def default_target_group_protocol
|
106
|
+
default_elb_protocol
|
107
|
+
end
|
108
|
+
|
109
|
+
def default_elb_protocol
|
110
|
+
@elb_type == "application" ? "HTTP" : "TCP"
|
111
|
+
end
|
112
|
+
|
113
|
+
def pretty_name?
|
114
|
+
# env variable takes highest precedence
|
115
|
+
if ENV["STATIC_NAME"]
|
116
|
+
ENV["STATIC_NAME"] != "0"
|
117
|
+
else
|
118
|
+
settings[:pretty_name]
|
44
119
|
end
|
45
|
-
hash
|
46
120
|
end
|
47
121
|
end
|
48
122
|
end
|