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.
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,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