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