moonshot 2.0.0.beta6 → 2.0.0.beta7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/bin/moonshot +4 -1
- data/lib/default/bin/build.sh +0 -0
- data/lib/moonshot/account_context.rb +2 -0
- data/lib/moonshot/always_use_default_source.rb +5 -4
- data/lib/moonshot/artifact_repository/s3_bucket.rb +11 -5
- data/lib/moonshot/artifact_repository/s3_bucket_via_github_releases.rb +7 -7
- data/lib/moonshot/ask_user_source.rb +2 -0
- data/lib/moonshot/build_mechanism/github_release.rb +9 -2
- data/lib/moonshot/build_mechanism/script.rb +8 -7
- data/lib/moonshot/build_mechanism/travis_deploy.rb +5 -5
- data/lib/moonshot/build_mechanism/version_proxy.rb +13 -0
- data/lib/moonshot/change_set.rb +17 -35
- data/lib/moonshot/command.rb +3 -1
- data/lib/moonshot/command_line.rb +12 -9
- data/lib/moonshot/command_line_dispatcher.rb +5 -7
- data/lib/moonshot/commands/build.rb +6 -0
- data/lib/moonshot/commands/console.rb +2 -0
- data/lib/moonshot/commands/create.rb +14 -2
- data/lib/moonshot/commands/delete.rb +9 -0
- data/lib/moonshot/commands/deploy.rb +4 -8
- data/lib/moonshot/commands/doctor.rb +2 -0
- data/lib/moonshot/commands/generate_template.rb +46 -0
- data/lib/moonshot/commands/interactive_command.rb +15 -0
- data/lib/moonshot/commands/list.rb +2 -0
- data/lib/moonshot/commands/new.rb +5 -2
- data/lib/moonshot/commands/parameter_arguments.rb +5 -4
- data/lib/moonshot/commands/parent_stack_option.rb +2 -0
- data/lib/moonshot/commands/push.rb +2 -0
- data/lib/moonshot/commands/show_all_events_option.rb +2 -0
- data/lib/moonshot/commands/ssh.rb +4 -0
- data/lib/moonshot/commands/status.rb +2 -0
- data/lib/moonshot/commands/update.rb +7 -1
- data/lib/moonshot/commands/version.rb +2 -0
- data/lib/moonshot/controller.rb +28 -13
- data/lib/moonshot/controller_config.rb +12 -27
- data/lib/moonshot/creds_helper.rb +2 -0
- data/lib/moonshot/deployment_mechanism/code_deploy.rb +44 -37
- data/lib/moonshot/doctor_helper.rb +14 -15
- data/lib/moonshot/dynamic_template.rb +76 -0
- data/lib/moonshot/interactive_logger_proxy.rb +4 -4
- data/lib/moonshot/json_stack_template.rb +3 -0
- data/lib/moonshot/parameter_collection.rb +3 -0
- data/lib/moonshot/parent_stack_parameter_loader.rb +7 -3
- data/lib/moonshot/resources.rb +2 -0
- data/lib/moonshot/resources_helper.rb +5 -1
- data/lib/moonshot/shell.rb +8 -8
- data/lib/moonshot/ssh_command.rb +2 -0
- data/lib/moonshot/ssh_command_builder.rb +3 -1
- data/lib/moonshot/ssh_config.rb +3 -2
- data/lib/moonshot/ssh_fork_executor.rb +2 -0
- data/lib/moonshot/ssh_target_selector.rb +3 -1
- data/lib/moonshot/stack.rb +50 -46
- data/lib/moonshot/stack_asg_printer.rb +14 -12
- data/lib/moonshot/stack_config.rb +3 -2
- data/lib/moonshot/stack_events_poller.rb +3 -1
- data/lib/moonshot/stack_list_printer.rb +2 -0
- data/lib/moonshot/stack_lister.rb +6 -2
- data/lib/moonshot/stack_output_printer.rb +2 -0
- data/lib/moonshot/stack_parameter.rb +5 -9
- data/lib/moonshot/stack_parameter_printer.rb +3 -1
- data/lib/moonshot/stack_template.rb +2 -0
- data/lib/moonshot/task.rb +3 -0
- data/lib/moonshot/tools/asg_rollout/asg.rb +19 -21
- data/lib/moonshot/tools/asg_rollout/asg_instance.rb +2 -0
- data/lib/moonshot/tools/asg_rollout/hook_exec_environment.rb +2 -0
- data/lib/moonshot/tools/asg_rollout/instance_health.rb +2 -0
- data/lib/moonshot/tools/asg_rollout.rb +16 -14
- data/lib/moonshot/tools/asg_rollout_config.rb +2 -0
- data/lib/moonshot/unicode_table.rb +1 -1
- data/lib/moonshot/yaml_stack_template.rb +2 -0
- data/lib/moonshot.rb +13 -1
- data/lib/plugins/backup.rb +24 -30
- data/lib/plugins/code_deploy_setup.rb +4 -2
- data/lib/plugins/dynamic_template.rb +36 -0
- data/lib/plugins/encrypted_parameters/kms_key.rb +2 -0
- data/lib/plugins/encrypted_parameters/parameter_encrypter.rb +2 -0
- data/lib/plugins/encrypted_parameters.rb +3 -1
- metadata +124 -57
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'erb'
|
4
|
+
|
5
|
+
module Moonshot
|
6
|
+
class TemplateExists < StandardError; end
|
7
|
+
class InvalidTemplate < StandardError; end
|
8
|
+
|
9
|
+
class DynamicTemplate
|
10
|
+
# A class to encapsulate template parameters, passing a hash to `process` is
|
11
|
+
# only available from Ruby 2.5.
|
12
|
+
class Parameters
|
13
|
+
def initialize(parameters)
|
14
|
+
parameters.each do |k, v|
|
15
|
+
instance_variable_set("@#{k}".to_sym, v)
|
16
|
+
# Adding an attribute reader for flexibility, this way you can add
|
17
|
+
# either `@parameter` or just `parameter` to your template.
|
18
|
+
self.class.send(:attr_reader, k.to_sym)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def expose_binding
|
23
|
+
binding
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
attr_writer :destination
|
28
|
+
|
29
|
+
def initialize(source:, parameters:, destination:)
|
30
|
+
@source = File.read(source)
|
31
|
+
@parameters = parameters
|
32
|
+
@destination = destination
|
33
|
+
end
|
34
|
+
|
35
|
+
def parameters_obj
|
36
|
+
@parameters_obj ||= Parameters.new(parameters_file)
|
37
|
+
end
|
38
|
+
|
39
|
+
def parameters_file
|
40
|
+
env_name = Moonshot.config.environment_name
|
41
|
+
@parameters.respond_to?(:call) ? @parameters.call(env_name) : @parameters
|
42
|
+
end
|
43
|
+
|
44
|
+
def process
|
45
|
+
validate_destination_exists
|
46
|
+
new_template = generate_template
|
47
|
+
|
48
|
+
validate_template(new_template)
|
49
|
+
write_output(new_template)
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def validate_destination_exists
|
55
|
+
return unless File.file?(@destination)
|
56
|
+
|
57
|
+
raise TemplateExists, "Output file '#{@destination}' already exists."
|
58
|
+
end
|
59
|
+
|
60
|
+
def validate_template(template)
|
61
|
+
Aws::CloudFormation::Client.new.validate_template(
|
62
|
+
template_body: template
|
63
|
+
)
|
64
|
+
rescue Aws::CloudFormation::Errors::ValidationError => e
|
65
|
+
raise InvalidTemplate, "Invalid template:\n#{e}"
|
66
|
+
end
|
67
|
+
|
68
|
+
def generate_template
|
69
|
+
ERB.new(@source).result(parameters_obj.expose_binding)
|
70
|
+
end
|
71
|
+
|
72
|
+
def write_output(content)
|
73
|
+
File.write(@destination, content)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'forwardable'
|
2
4
|
|
3
5
|
module Moonshot
|
@@ -10,8 +12,7 @@ module Moonshot
|
|
10
12
|
@logger = logger
|
11
13
|
end
|
12
14
|
|
13
|
-
def blank
|
14
|
-
end
|
15
|
+
def blank; end
|
15
16
|
|
16
17
|
def continue(str = nil)
|
17
18
|
@logger.info(str) if str
|
@@ -21,8 +22,7 @@ module Moonshot
|
|
21
22
|
@logger.error(str)
|
22
23
|
end
|
23
24
|
|
24
|
-
def repaint
|
25
|
-
end
|
25
|
+
def repaint; end
|
26
26
|
|
27
27
|
def success(str = 'Success')
|
28
28
|
@logger.info(str)
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Moonshot
|
2
4
|
# A Rigid Hash-like structure that only accepts manipulation of
|
3
5
|
# parameters defined in the Stack template. Anything else results in
|
@@ -6,6 +8,7 @@ module Moonshot
|
|
6
8
|
extend Forwardable
|
7
9
|
|
8
10
|
def_delegators :@hash, :key?, :fetch, :[], :keys, :values
|
11
|
+
attr_reader :hash
|
9
12
|
|
10
13
|
def self.from_template(template)
|
11
14
|
obj = new
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Moonshot
|
2
4
|
class ParentStackParameterLoader
|
3
5
|
def initialize(config)
|
@@ -8,30 +10,32 @@ module Moonshot
|
|
8
10
|
@config.parent_stacks.each do |stack_name|
|
9
11
|
count = 0
|
10
12
|
|
11
|
-
resp = cf_client.describe_stacks(stack_name:
|
13
|
+
resp = cf_client.describe_stacks(stack_name:)
|
12
14
|
raise "Parent Stack #{stack_name} not found!" unless resp.stacks.size == 1
|
13
15
|
|
14
16
|
# If there is an input parameters matching a stack output, pass it.
|
15
17
|
resp.stacks[0].outputs.each do |output|
|
16
18
|
next unless @config.parameters.key?(output.output_key)
|
19
|
+
|
17
20
|
# Our Stack has a Parameter matching this output. Set it's
|
18
21
|
# value to the Output's value.
|
19
22
|
count += 1
|
20
23
|
@config.parameters.fetch(output.output_key).set(output.output_value)
|
21
24
|
end
|
22
25
|
|
23
|
-
puts "Imported #{count} parameters from parent stack #{stack_name.blue}!" if count
|
26
|
+
puts "Imported #{count} parameters from parent stack #{stack_name.blue}!" if count.positive?
|
24
27
|
end
|
25
28
|
end
|
26
29
|
|
27
30
|
def load_missing_only!
|
28
31
|
@config.parent_stacks.each do |stack_name|
|
29
|
-
resp = cf_client.describe_stacks(stack_name:
|
32
|
+
resp = cf_client.describe_stacks(stack_name:)
|
30
33
|
raise "Parent Stack #{stack_name} not found!" unless resp.stacks.size == 1
|
31
34
|
|
32
35
|
# If there is an input parameters matching a stack output, pass it.
|
33
36
|
resp.stacks[0].outputs.each do |output|
|
34
37
|
next unless @config.parameters.key?(output.output_key)
|
38
|
+
|
35
39
|
# Our Stack has a Parameter matching this output. Set it's
|
36
40
|
# value to the Output's value, but only if we don't already
|
37
41
|
# have a previous value we're using.
|
data/lib/moonshot/resources.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Moonshot
|
2
4
|
# Provides shorthand methods for accessing resources provided by the Resources
|
3
5
|
# container.
|
@@ -8,16 +10,18 @@ module Moonshot
|
|
8
10
|
|
9
11
|
# TODO: Deprecate this interface.
|
10
12
|
def log
|
11
|
-
@log ||= Logger.new(
|
13
|
+
@log ||= Logger.new($stdout)
|
12
14
|
end
|
13
15
|
|
14
16
|
def stack
|
15
17
|
raise 'Resources not provided to Mechanism!' unless @resources
|
18
|
+
|
16
19
|
@resources.stack
|
17
20
|
end
|
18
21
|
|
19
22
|
def ilog
|
20
23
|
raise 'Resources not provided to Mechanism!' unless @resources
|
24
|
+
|
21
25
|
@resources.ilog
|
22
26
|
end
|
23
27
|
end
|
data/lib/moonshot/shell.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'thor'
|
2
4
|
require 'retriable'
|
3
5
|
|
@@ -17,7 +19,7 @@ module Moonshot::Shell
|
|
17
19
|
|
18
20
|
# Run a command, returning stdout. Stderr is suppressed unless the command
|
19
21
|
# returns non-zero.
|
20
|
-
def sh_out(cmd, fail
|
22
|
+
def sh_out(cmd, fail = true, stdin = '')
|
21
23
|
r_in, w_in = IO.pipe
|
22
24
|
r_out, w_out = IO.pipe
|
23
25
|
r_err, w_err = IO.pipe
|
@@ -53,13 +55,11 @@ module Moonshot::Shell
|
|
53
55
|
define_method(meth) { |*args| shell.public_send(meth, *args) }
|
54
56
|
end
|
55
57
|
|
56
|
-
def sh_step(cmd, args
|
58
|
+
def sh_step(cmd, **args)
|
57
59
|
msg = args.delete(:msg) || cmd
|
58
|
-
if msg.length > (terminal_width - 18)
|
59
|
-
msg = "#{msg[0..(terminal_width - 22)]}..."
|
60
|
-
end
|
60
|
+
msg = "#{msg[0..(terminal_width - 22)]}..." if msg.length > (terminal_width - 18)
|
61
61
|
ilog.start_threaded(msg) do |step|
|
62
|
-
out = sh_out(cmd, args)
|
62
|
+
out = sh_out(cmd, **args)
|
63
63
|
yield step, out if block_given?
|
64
64
|
step.success
|
65
65
|
end
|
@@ -73,9 +73,9 @@ module Moonshot::Shell
|
|
73
73
|
# @param opts [Hash] Options for retriable.
|
74
74
|
#
|
75
75
|
# @return [String] Stdout form the command.
|
76
|
-
def sh_retry(cmd, fail
|
76
|
+
def sh_retry(cmd, fail = true, stdin = '', opts: {})
|
77
77
|
Retriable.retriable(DEFAULT_RETRY_OPTIONS.merge(opts)) do
|
78
|
-
out = sh_out(cmd, stdin:
|
78
|
+
out = sh_out(cmd, stdin:)
|
79
79
|
yield out if block_given?
|
80
80
|
out
|
81
81
|
end
|
data/lib/moonshot/ssh_command.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'shellwords'
|
2
4
|
|
3
5
|
module Moonshot
|
@@ -25,7 +27,7 @@ module Moonshot
|
|
25
27
|
@instance_ip ||= Aws::EC2::Client.new
|
26
28
|
.describe_instances(instance_ids: [@instance_id])
|
27
29
|
.reservations.first.instances.first.public_ip_address
|
28
|
-
rescue
|
30
|
+
rescue StandardError
|
29
31
|
raise "Failed to determine public IP address for instance #{@instance_id}!"
|
30
32
|
end
|
31
33
|
end
|
data/lib/moonshot/ssh_config.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Moonshot
|
2
4
|
# Choose a publically accessible instance to run commands on, given a Moonshot::Stack.
|
3
5
|
class SSHTargetSelector
|
@@ -23,7 +25,7 @@ module Moonshot
|
|
23
25
|
Aws::AutoScaling::Client.new.describe_auto_scaling_groups(
|
24
26
|
auto_scaling_group_names: [asg.physical_resource_id]
|
25
27
|
).auto_scaling_groups.first.instances.map(&:instance_id).first
|
26
|
-
rescue => e
|
28
|
+
rescue StandardError => e
|
27
29
|
raise "Failed to select an instance from the Auto Scaling Group: #{e.message}"
|
28
30
|
end
|
29
31
|
end
|
data/lib/moonshot/stack.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'yaml'
|
2
4
|
|
3
5
|
module Moonshot
|
@@ -5,12 +7,11 @@ module Moonshot
|
|
5
7
|
# stores the state of the active stack running on AWS, but contains a
|
6
8
|
# reference to the StackTemplate that would be applied with an update
|
7
9
|
# action.
|
8
|
-
class Stack # rubocop:disable ClassLength
|
10
|
+
class Stack # rubocop:disable Metrics/ClassLength
|
9
11
|
include CredsHelper
|
10
12
|
include DoctorHelper
|
11
13
|
|
12
|
-
attr_reader :app_name
|
13
|
-
attr_reader :name
|
14
|
+
attr_reader :app_name, :name
|
14
15
|
|
15
16
|
def initialize(config)
|
16
17
|
@config = config
|
@@ -111,7 +112,7 @@ module Moonshot
|
|
111
112
|
resource_summary = resource_summaries.find do |r|
|
112
113
|
r.logical_resource_id == logical_id
|
113
114
|
end
|
114
|
-
resource_summary
|
115
|
+
resource_summary&.physical_resource_id
|
115
116
|
end
|
116
117
|
|
117
118
|
# @return [Array<Aws::CloudFormation::Types::StackResourceSummary>]
|
@@ -152,13 +153,23 @@ module Moonshot
|
|
152
153
|
|
153
154
|
# Support the legacy file location from Moonshot 1.0.
|
154
155
|
YamlStackTemplate.new(
|
155
|
-
File.join(@config.project_root, 'cloud_formation', "#{@config.app_name}.yml")
|
156
|
+
File.join(@config.project_root, 'cloud_formation', "#{@config.app_name}.yml")
|
157
|
+
),
|
156
158
|
JsonStackTemplate.new(
|
157
|
-
File.join(@config.project_root, 'cloud_formation', "#{@config.app_name}.json")
|
159
|
+
File.join(@config.project_root, 'cloud_formation', "#{@config.app_name}.json")
|
160
|
+
)
|
158
161
|
]
|
159
162
|
|
163
|
+
# If a template file has been specified in the config, look there first.
|
164
|
+
if @config.template_file
|
165
|
+
templates.unshift YamlStackTemplate.new(@config.template_file)
|
166
|
+
templates.unshift JsonStackTemplate.new(@config.template_file)
|
167
|
+
end
|
168
|
+
|
160
169
|
template = templates.find(&:exist?)
|
170
|
+
|
161
171
|
raise 'No template found in moonshot/template.{yml,json}!' unless template
|
172
|
+
|
162
173
|
template
|
163
174
|
end
|
164
175
|
|
@@ -177,9 +188,7 @@ module Moonshot
|
|
177
188
|
end
|
178
189
|
|
179
190
|
def upload_template_to_s3
|
180
|
-
unless @config.template_s3_bucket
|
181
|
-
raise 'The S3 bucket to store the template in is not configured.'
|
182
|
-
end
|
191
|
+
raise 'The S3 bucket to store the template in is not configured.' unless @config.template_s3_bucket
|
183
192
|
|
184
193
|
s3_object_key = "#{@name}-#{Time.now.getutc.to_i}-#{File.basename(template.filename)}"
|
185
194
|
template_url = "http://#{@config.template_s3_bucket}.s3.amazonaws.com/#{s3_object_key}"
|
@@ -199,7 +208,7 @@ module Moonshot
|
|
199
208
|
def create_stack
|
200
209
|
parameters = {
|
201
210
|
stack_name: @name,
|
202
|
-
capabilities: %w
|
211
|
+
capabilities: %w[CAPABILITY_IAM CAPABILITY_NAMED_IAM],
|
203
212
|
parameters: @config.parameters.values.map(&:to_cf),
|
204
213
|
tags: make_tags
|
205
214
|
}
|
@@ -221,10 +230,10 @@ module Moonshot
|
|
221
230
|
].join('-')
|
222
231
|
|
223
232
|
parameters = {
|
224
|
-
change_set_name
|
233
|
+
change_set_name:,
|
225
234
|
description: "Moonshot update command for application '#{Moonshot.config.app_name}'",
|
226
235
|
stack_name: @name,
|
227
|
-
capabilities:
|
236
|
+
capabilities: %w[CAPABILITY_IAM CAPABILITY_NAMED_IAM],
|
228
237
|
parameters: @config.parameters.values.map(&:to_cf)
|
229
238
|
}
|
230
239
|
if @config.template_s3_bucket
|
@@ -248,36 +257,31 @@ module Moonshot
|
|
248
257
|
events.show_only_errors unless @config.show_all_stack_events
|
249
258
|
|
250
259
|
@ilog.start_threaded "Waiting for #{stack_name} to be successfully #{past_tense_verb}." do |s|
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
# Do nothing. The above event logging block may result in
|
261
|
-
# a ValidationError while waiting for a stack to delete.
|
262
|
-
end
|
263
|
-
# rubocop:enable Lint/HandleExceptions
|
264
|
-
|
265
|
-
if attempt == w.max_attempts - 1
|
266
|
-
s.failure "#{stack_name} was not #{past_tense_verb} after 30 minutes."
|
267
|
-
result = false
|
268
|
-
|
269
|
-
# We don't want the interactive logger to catch an exception.
|
270
|
-
throw :success
|
271
|
-
end
|
272
|
-
s.continue "Waiting for CloudFormation Stack to be successfully #{past_tense_verb}, current status '#{resp.stacks.first.stack_status}'." # rubocop:disable LineLength
|
260
|
+
cf_client.wait_until(wait_target, stack_name: stack_id) do |w|
|
261
|
+
w.delay = 10
|
262
|
+
w.max_attempts = 360 # 60 minutes.
|
263
|
+
w.before_wait do |attempt, resp|
|
264
|
+
begin
|
265
|
+
events.latest_events.each { |e| @ilog.error(format_event(e)) }
|
266
|
+
rescue Aws::CloudFormation::Errors::ValidationError
|
267
|
+
# Do nothing. The above event logging block may result in
|
268
|
+
# a ValidationError while waiting for a stack to delete.
|
273
269
|
end
|
274
|
-
|
270
|
+
if attempt == w.max_attempts - 1
|
271
|
+
s.failure "#{stack_name} was not #{past_tense_verb} after 30 minutes."
|
272
|
+
result = false
|
275
273
|
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
274
|
+
# We don't want the interactive logger to catch an exception.
|
275
|
+
throw :success
|
276
|
+
end
|
277
|
+
s.continue "Waiting for CloudFormation Stack to be successfully #{past_tense_verb}, current status '#{resp.stacks.first.stack_status}'." # rubocop:disable Layout/LineLength
|
278
|
+
end
|
280
279
|
end
|
280
|
+
|
281
|
+
s.success "#{stack_name} successfully #{past_tense_verb}." if result
|
282
|
+
rescue Aws::Waiters::Errors::FailureStateError
|
283
|
+
result = false
|
284
|
+
s.failure "#{stack_name} failed to update."
|
281
285
|
end
|
282
286
|
|
283
287
|
result
|
@@ -289,9 +293,7 @@ module Moonshot
|
|
289
293
|
{ key: 'moonshot_environment', value: @config.environment_name }
|
290
294
|
]
|
291
295
|
|
292
|
-
if @config.additional_tag
|
293
|
-
default_tags << { key: @config.additional_tag, value: @name }
|
294
|
-
end
|
296
|
+
default_tags << { key: @config.additional_tag, value: @name } if @config.additional_tag
|
295
297
|
|
296
298
|
default_tags
|
297
299
|
end
|
@@ -320,14 +322,15 @@ module Moonshot
|
|
320
322
|
end
|
321
323
|
|
322
324
|
def doctor_check_template_against_aws
|
325
|
+
validate_params = {}
|
323
326
|
if @config.template_s3_bucket
|
324
|
-
|
327
|
+
validate_params[:template_url] = upload_template_to_s3
|
325
328
|
else
|
326
|
-
|
329
|
+
validate_params[:template_body] = template.body
|
327
330
|
end
|
328
|
-
cf_client.validate_template(
|
331
|
+
cf_client.validate_template(validate_params)
|
329
332
|
success('CloudFormation template is valid.')
|
330
|
-
rescue => e
|
333
|
+
rescue StandardError => e
|
331
334
|
critical('Invalid CloudFormation template!', e.message)
|
332
335
|
end
|
333
336
|
|
@@ -351,6 +354,7 @@ module Moonshot
|
|
351
354
|
|
352
355
|
success = wait_for_stack_state(:stack_update_complete, 'updated')
|
353
356
|
raise 'Failed to update the CloudFormation Stack.' unless success
|
357
|
+
|
354
358
|
success
|
355
359
|
end
|
356
360
|
end
|
@@ -1,4 +1,5 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'colorize'
|
3
4
|
require 'ruby-duration'
|
4
5
|
|
@@ -16,8 +17,8 @@ module Moonshot
|
|
16
17
|
def print
|
17
18
|
asgs.each do |asg|
|
18
19
|
asg_info = as_client.describe_auto_scaling_groups(
|
19
|
-
auto_scaling_group_names: [asg.physical_resource_id]
|
20
|
-
|
20
|
+
auto_scaling_group_names: [asg.physical_resource_id]
|
21
|
+
).auto_scaling_groups.first
|
21
22
|
t_asg_info = @table.add_leaf("ASG: #{asg.logical_resource_id}")
|
22
23
|
|
23
24
|
add_asg_info(t_asg_info, asg_info)
|
@@ -70,7 +71,7 @@ module Moonshot
|
|
70
71
|
|
71
72
|
# Get additional information about instances not returned by the ASG API.
|
72
73
|
def get_addl_info(instance_ids)
|
73
|
-
resp = ec2_client.describe_instances(instance_ids:
|
74
|
+
resp = ec2_client.describe_instances(instance_ids:)
|
74
75
|
|
75
76
|
data = {}
|
76
77
|
resp.reservations.map(&:instances).flatten.each do |instance|
|
@@ -85,7 +86,7 @@ module Moonshot
|
|
85
86
|
|
86
87
|
hc = asg_info.health_check_type.blue
|
87
88
|
gp = (asg_info.health_check_grace_period.to_s << 's').blue
|
88
|
-
table.add_line "Using #{hc} health checks, with a #{gp} health check grace period."
|
89
|
+
table.add_line "Using #{hc} health checks, with a #{gp} health check grace period."
|
89
90
|
|
90
91
|
dc = asg_info.desired_capacity.to_s.blue
|
91
92
|
min = asg_info.min_size.to_s.blue
|
@@ -93,7 +94,7 @@ module Moonshot
|
|
93
94
|
table.add_line "Desired Capacity is #{dc} (Min: #{min}, Max: #{max})."
|
94
95
|
|
95
96
|
lbs = asg_info.load_balancer_names
|
96
|
-
table.add_line "Has #{lbs.count.to_s.blue} Load Balancer(s): #{lbs.map(&:blue).join(', ')}"
|
97
|
+
table.add_line "Has #{lbs.count.to_s.blue} Load Balancer(s): #{lbs.map(&:blue).join(', ')}"
|
97
98
|
end
|
98
99
|
|
99
100
|
def create_instance_table(asg_info)
|
@@ -112,11 +113,11 @@ module Moonshot
|
|
112
113
|
|
113
114
|
def instance_row(asg_instance, ec2_instance)
|
114
115
|
if ec2_instance
|
115
|
-
if ec2_instance.public_ip_address
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
116
|
+
ip_address = if ec2_instance.public_ip_address
|
117
|
+
"#{ec2_instance.public_ip_address} (#{ec2_instance.private_ip_address})"
|
118
|
+
else
|
119
|
+
"#{ec2_instance.private_ip_address} (PRV)"
|
120
|
+
end
|
120
121
|
uptime = uptime_format(ec2_instance.launch_time)
|
121
122
|
else
|
122
123
|
# We've seen race conditions where ASG tells us about instances that EC2 is no longer
|
@@ -142,7 +143,8 @@ module Moonshot
|
|
142
143
|
def add_recent_activity_leaf(table, asg_name)
|
143
144
|
recent = table.add_leaf('Recent Activity')
|
144
145
|
resp = as_client.describe_scaling_activities(
|
145
|
-
auto_scaling_group_name: asg_name
|
146
|
+
auto_scaling_group_name: asg_name
|
147
|
+
).activities
|
146
148
|
|
147
149
|
rows = resp.sort_by(&:start_time).reverse.first(10).map do |activity|
|
148
150
|
row_for_activity(activity)
|
@@ -1,8 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Moonshot
|
2
4
|
# Configuration for the Moonshot::Stack class.
|
3
5
|
class StackConfig
|
4
|
-
attr_accessor :parent_stacks
|
5
|
-
attr_accessor :show_all_events
|
6
|
+
attr_accessor :parent_stacks, :show_all_events
|
6
7
|
|
7
8
|
def initialize
|
8
9
|
@parent_stacks = []
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Moonshot
|
2
4
|
# The StackEventsPoller queries DescribeStackEvents every time #latest_events
|
3
5
|
# is invoked, filtering out events that have already been returned. It can
|
@@ -32,7 +34,7 @@ module Moonshot
|
|
32
34
|
def filter_events(events)
|
33
35
|
if @errors_only
|
34
36
|
events.select do |event|
|
35
|
-
%w
|
37
|
+
%w[CREATE_FAILED UPDATE_FAILED DELETE_FAILED].include?(event.resource_status)
|
36
38
|
end
|
37
39
|
else
|
38
40
|
events
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Moonshot
|
2
4
|
# The StackLister is world renoun for it's ability to list stacks.
|
3
5
|
class StackLister
|
@@ -13,7 +15,7 @@ module Moonshot
|
|
13
15
|
result = []
|
14
16
|
next_token = nil
|
15
17
|
loop do
|
16
|
-
resp = cf_client.describe_stacks(next_token:
|
18
|
+
resp = cf_client.describe_stacks(next_token:)
|
17
19
|
resp.stacks.each do |stack|
|
18
20
|
app_tag = stack.tags.find { |t| t.key == 'moonshot_application' }
|
19
21
|
env_tag = stack.tags.find { |t| t.key == 'moonshot_environment' }
|
@@ -22,15 +24,17 @@ module Moonshot
|
|
22
24
|
if app_tag && app_tag.value == Moonshot.config.app_name
|
23
25
|
result <<
|
24
26
|
EnvironmentDescription.new(env_tag.value, stack.creation_time, stack.stack_status)
|
25
|
-
elsif legacy_tag
|
27
|
+
elsif legacy_tag&.value&.start_with?(Moonshot.config.app_name)
|
26
28
|
result <<
|
27
29
|
EnvironmentDescription.new(legacy_tag.value, stack.creation_time, stack.stack_status)
|
28
30
|
end
|
29
31
|
end
|
30
32
|
break unless resp.next_token
|
33
|
+
|
31
34
|
next_token = resp.next_token
|
32
35
|
end
|
33
36
|
result.sort_by(&:name)
|
34
37
|
end
|
38
|
+
# rubocop:enable Metrics/AbcSize
|
35
39
|
end
|
36
40
|
end
|
@@ -1,8 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Moonshot
|
2
4
|
class StackParameter
|
3
|
-
attr_reader :name
|
4
|
-
attr_reader :default
|
5
|
-
attr_reader :description
|
5
|
+
attr_reader :name, :default, :description
|
6
6
|
|
7
7
|
def initialize(name, default: nil, use_previous: false, description: '')
|
8
8
|
@default = default
|
@@ -32,9 +32,7 @@ module Moonshot
|
|
32
32
|
end
|
33
33
|
|
34
34
|
def use_previous!(value)
|
35
|
-
if @value
|
36
|
-
raise "Value already set for StackParameter #{@name}, cannot use previous value!"
|
37
|
-
end
|
35
|
+
raise "Value already set for StackParameter #{@name}, cannot use previous value!" if @value
|
38
36
|
|
39
37
|
# Make the current value available to plugins.
|
40
38
|
@value = value
|
@@ -42,9 +40,7 @@ module Moonshot
|
|
42
40
|
end
|
43
41
|
|
44
42
|
def value
|
45
|
-
unless @value || default?
|
46
|
-
raise "No value set and no default for StackParameter #{@name}!"
|
47
|
-
end
|
43
|
+
raise "No value set and no default for StackParameter #{@name}!" unless @value || default?
|
48
44
|
|
49
45
|
@value || default
|
50
46
|
end
|