ufo 3.5.7 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (191) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +24 -0
  3. data/Gemfile.lock +16 -10
  4. data/README.md +12 -13
  5. data/docs/_config.yml +1 -1
  6. data/docs/_docs/auto-completion.md +4 -4
  7. data/docs/_docs/automated-cleanup.md +1 -1
  8. data/docs/_docs/conventions.md +7 -7
  9. data/docs/_docs/customize-cloudformation.md +36 -0
  10. data/docs/_docs/faq.md +9 -7
  11. data/docs/_docs/fargate.md +102 -0
  12. data/docs/_docs/helpers.md +3 -3
  13. data/docs/_docs/load-balancer.md +72 -0
  14. data/docs/_docs/migrations.md +2 -2
  15. data/docs/_docs/next-steps.md +2 -2
  16. data/docs/_docs/params.md +12 -41
  17. data/docs/_docs/route53-support.md +28 -0
  18. data/docs/_docs/run-in-pieces.md +2 -2
  19. data/docs/_docs/security-groups.md +54 -0
  20. data/docs/_docs/settings-cfn.md +11 -0
  21. data/docs/_docs/settings-network.md +34 -0
  22. data/docs/_docs/settings.md +18 -15
  23. data/docs/_docs/single-task.md +3 -3
  24. data/docs/_docs/ssl-support.md +42 -0
  25. data/docs/_docs/structure.md +5 -1
  26. data/docs/_docs/stuck-cloudformation.md +30 -0
  27. data/docs/_docs/tutorial-ufo-docker-build.md +19 -31
  28. data/docs/_docs/tutorial-ufo-init.md +16 -12
  29. data/docs/_docs/tutorial-ufo-ship.md +50 -54
  30. data/docs/_docs/tutorial-ufo-ships.md +9 -7
  31. data/docs/_docs/tutorial-ufo-tasks-build.md +26 -17
  32. data/docs/_docs/ufo-current.md +50 -0
  33. data/docs/_docs/ufo-env-extra.md +21 -0
  34. data/docs/_docs/ufo-env.md +6 -13
  35. data/docs/_docs/ufo-tasks-register.md +3 -3
  36. data/docs/_docs/upgrade4.md +49 -0
  37. data/docs/_docs/variables.md +5 -5
  38. data/docs/_docs/why-cloudformation.md +22 -0
  39. data/docs/_includes/about.html +1 -1
  40. data/docs/_includes/cfn-customize.md +39 -0
  41. data/docs/_includes/commands.html +6 -6
  42. data/docs/_includes/css/ufo.css +1 -0
  43. data/docs/_includes/example.html +13 -13
  44. data/docs/_includes/reference.md +1 -1
  45. data/docs/_includes/subnav.html +22 -5
  46. data/docs/_includes/ufo-ship-options.md +7 -6
  47. data/docs/_reference/ufo-apps.md +36 -0
  48. data/docs/_reference/ufo-cancel.md +24 -0
  49. data/docs/_reference/ufo-completion.md +1 -1
  50. data/docs/_reference/ufo-completion_script.md +1 -1
  51. data/docs/_reference/ufo-current.md +93 -0
  52. data/docs/_reference/ufo-deploy.md +18 -17
  53. data/docs/_reference/ufo-destroy.md +6 -4
  54. data/docs/_reference/ufo-docker-base.md +7 -7
  55. data/docs/_reference/ufo-docker-build.md +9 -9
  56. data/docs/_reference/ufo-docker-clean.md +8 -8
  57. data/docs/_reference/ufo-docker-name.md +4 -4
  58. data/docs/_reference/ufo-docker.md +4 -2
  59. data/docs/_reference/ufo-init.md +31 -20
  60. data/docs/_reference/ufo-network-help.md +15 -0
  61. data/docs/_reference/ufo-network-init.md +38 -0
  62. data/docs/_reference/ufo-network.md +26 -0
  63. data/docs/_reference/ufo-ps.md +53 -0
  64. data/docs/_reference/ufo-releases.md +40 -0
  65. data/docs/_reference/ufo-resources.md +44 -0
  66. data/docs/_reference/ufo-rollback.md +59 -0
  67. data/docs/_reference/ufo-scale.md +23 -3
  68. data/docs/_reference/ufo-ship.md +54 -27
  69. data/docs/_reference/ufo-ships.md +17 -26
  70. data/docs/_reference/ufo-stop.md +31 -0
  71. data/docs/_reference/ufo-task.md +15 -16
  72. data/docs/_reference/ufo-tasks-build.md +10 -10
  73. data/docs/_reference/ufo-tasks-register.md +3 -3
  74. data/docs/_reference/ufo-tasks.md +1 -1
  75. data/docs/_reference/ufo-upgrade-help.md +15 -0
  76. data/docs/_reference/ufo-upgrade-v2to3.md +15 -0
  77. data/docs/_reference/ufo-upgrade-v3_3to3_4.md +15 -0
  78. data/docs/_reference/ufo-upgrade-v3to4.md +27 -0
  79. data/docs/_reference/ufo-upgrade.md +28 -0
  80. data/docs/_reference/ufo-version.md +1 -1
  81. data/docs/articles.md +2 -2
  82. data/docs/docs.md +1 -1
  83. data/docs/img/docs/cloudformation-resources.png +0 -0
  84. data/docs/img/tutorials/ecs-console-task-definitions.png +0 -0
  85. data/docs/img/tutorials/ecs-console-ufo-ship.png +0 -0
  86. data/docs/img/tutorials/ecs-console-ufo-ships.png +0 -0
  87. data/docs/quick-start.md +21 -9
  88. data/docs/reference.md +10 -2
  89. data/exe/ufo +1 -1
  90. data/lib/cfn/stack.yml +259 -0
  91. data/lib/template/.ufo/params.yml.tt +21 -60
  92. data/lib/template/.ufo/settings.yml.tt +6 -1
  93. data/lib/template/.ufo/settings/cfn/default.yml.tt +55 -0
  94. data/lib/template/.ufo/settings/network/default.yml.tt +18 -0
  95. data/lib/template/.ufo/task_definitions.rb.tt +7 -6
  96. data/lib/template/.ufo/templates/fargate.json.erb +1 -1
  97. data/lib/template/.ufo/templates/main.json.erb +1 -0
  98. data/lib/template/.ufo/variables/base.rb.tt +5 -2
  99. data/lib/template/Dockerfile +10 -15
  100. data/lib/template/bin/deploy.tt +2 -2
  101. data/lib/ufo.rb +29 -20
  102. data/lib/ufo/apps.rb +49 -0
  103. data/lib/ufo/apps/cfn_map.rb +70 -0
  104. data/lib/ufo/apps/service.rb +56 -0
  105. data/lib/ufo/aws_service.rb +15 -6
  106. data/lib/ufo/base.rb +32 -0
  107. data/lib/ufo/cancel.rb +23 -0
  108. data/lib/ufo/cli.rb +91 -27
  109. data/lib/ufo/core.rb +35 -3
  110. data/lib/ufo/current.rb +104 -0
  111. data/lib/ufo/destroy.rb +10 -41
  112. data/lib/ufo/docker/builder.rb +5 -4
  113. data/lib/ufo/docker/cleaner.rb +1 -1
  114. data/lib/ufo/docker/pusher.rb +2 -2
  115. data/lib/ufo/ecr/cleaner.rb +1 -1
  116. data/lib/ufo/help/apps.md +12 -0
  117. data/lib/ufo/help/balancer.md +3 -0
  118. data/lib/ufo/help/current.md +65 -0
  119. data/lib/ufo/help/deploy.md +4 -4
  120. data/lib/ufo/help/destroy.md +3 -3
  121. data/lib/ufo/help/docker.md +3 -1
  122. data/lib/ufo/help/docker/base.md +7 -7
  123. data/lib/ufo/help/docker/build.md +9 -9
  124. data/lib/ufo/help/docker/clean.md +8 -8
  125. data/lib/ufo/help/docker/name.md +4 -4
  126. data/lib/ufo/help/help.md +5 -0
  127. data/lib/ufo/help/init.md +24 -16
  128. data/lib/ufo/help/network/init.md +13 -0
  129. data/lib/ufo/help/ps.md +27 -0
  130. data/lib/ufo/help/releases.md +16 -0
  131. data/lib/ufo/help/resources.md +20 -0
  132. data/lib/ufo/help/rollback.md +35 -0
  133. data/lib/ufo/help/scale.md +22 -2
  134. data/lib/ufo/help/ship.md +40 -14
  135. data/lib/ufo/help/ships.md +4 -13
  136. data/lib/ufo/help/stop.md +7 -0
  137. data/lib/ufo/help/task.md +9 -9
  138. data/lib/ufo/help/tasks/build.md +10 -10
  139. data/lib/ufo/help/tasks/register.md +3 -3
  140. data/lib/ufo/help/upgrade/v3to4.md +3 -0
  141. data/lib/ufo/info.rb +62 -0
  142. data/lib/ufo/init.rb +36 -23
  143. data/lib/ufo/log_group.rb +2 -1
  144. data/lib/ufo/network.rb +24 -0
  145. data/lib/ufo/network/fetch.rb +41 -0
  146. data/lib/ufo/network/helper.rb +23 -0
  147. data/lib/ufo/network/init.rb +26 -0
  148. data/lib/ufo/param.rb +5 -5
  149. data/lib/ufo/ps.rb +102 -0
  150. data/lib/ufo/ps/task.rb +78 -0
  151. data/lib/ufo/releases.rb +14 -0
  152. data/lib/ufo/rollback.rb +53 -0
  153. data/lib/ufo/scale.rb +6 -12
  154. data/lib/ufo/sequence.rb +7 -0
  155. data/lib/ufo/setting.rb +7 -6
  156. data/lib/ufo/setting/profile.rb +24 -0
  157. data/lib/ufo/ship.rb +35 -326
  158. data/lib/ufo/stack.rb +203 -0
  159. data/lib/ufo/stack/context.rb +242 -0
  160. data/lib/ufo/stack/helper.rb +28 -0
  161. data/lib/ufo/stack/status.rb +195 -0
  162. data/lib/ufo/stop.rb +47 -0
  163. data/lib/ufo/task.rb +96 -15
  164. data/lib/ufo/tasks/register.rb +1 -1
  165. data/lib/ufo/template_scope.rb +81 -7
  166. data/lib/ufo/upgrade.rb +32 -0
  167. data/lib/ufo/{upgrade3.rb → upgrade/upgrade3.rb} +1 -1
  168. data/lib/ufo/{upgrade33_to_34.rb → upgrade/upgrade33to34.rb} +2 -2
  169. data/lib/ufo/upgrade/upgrade4.rb +161 -0
  170. data/lib/ufo/util.rb +19 -6
  171. data/lib/ufo/version.rb +1 -1
  172. data/spec/fixtures/apps/describe_services.json +96 -0
  173. data/spec/fixtures/cfn/stack-events-complete.json +1080 -0
  174. data/spec/fixtures/cfn/stack-events-in-progress.json +1080 -0
  175. data/spec/fixtures/cfn/stack-events-update-rollback-complete.json +1086 -0
  176. data/spec/fixtures/deployments.json +50 -0
  177. data/spec/fixtures/ps/describe_tasks.json +58 -0
  178. data/spec/fixtures/settings.yml +2 -0
  179. data/spec/lib/apps_spec.rb +20 -0
  180. data/spec/lib/cli_spec.rb +4 -4
  181. data/spec/lib/ps_spec.rb +14 -0
  182. data/spec/lib/setting_spec.rb +2 -1
  183. data/spec/lib/ship_spec.rb +6 -30
  184. data/spec/lib/stack/status_spec.rb +76 -0
  185. data/spec/lib/stop_spec.rb +13 -0
  186. data/spec/lib/task_spec.rb +5 -2
  187. data/spec/spec_helper.rb +1 -1
  188. data/ufo.gemspec +2 -0
  189. metadata +120 -6
  190. data/docs/_reference/ufo-upgrade3.md +0 -23
  191. 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
@@ -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
@@ -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.merge(default_params[:run_task] || {})
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 = original_container_definition[:log_configuration]
63
- container_name = original_container_definition[: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
- def original_container_definition
95
- resp = ecs.list_task_definitions(
96
- family_prefix: @task_definition,
97
- sort: "DESC"
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 = resp.task_definition_arns.first
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
- container_definition = resp.task_definition.container_definitions[0].to_h
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
@@ -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/hi-web-prod.json
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)
@@ -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
- def assign_instance_variables
39
- # copy over the instance variables to make available in RenderMePretty's scope
40
- hash = {}
41
- instance_variables.each do |var|
42
- key = var.to_s.sub('@','') # rid of the leading @
43
- hash[key.to_sym] = instance_variable_get(var)
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