moonshot 2.0.0.beta6 → 2.0.0.beta7
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.
- 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
|