moonshot 2.0.0.beta6 → 3.0.4
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 +24 -34
- 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 +16 -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/tag_arguments.rb +20 -0
- data/lib/moonshot/commands/update.rb +8 -1
- data/lib/moonshot/commands/version.rb +2 -0
- data/lib/moonshot/controller.rb +28 -13
- data/lib/moonshot/controller_config.rb +13 -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 +73 -55
- 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 +22 -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 +5 -3
- 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 +26 -5
- data/lib/plugins/encrypted_parameters/parameter_encrypter.rb +2 -0
- data/lib/plugins/encrypted_parameters.rb +6 -2
- metadata +189 -51
@@ -1,4 +1,5 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'colorize'
|
3
4
|
|
4
5
|
module Moonshot
|
@@ -19,18 +20,16 @@ module Moonshot
|
|
19
20
|
puts
|
20
21
|
puts self.class.name.split('::').last
|
21
22
|
private_methods.each do |meth|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
break
|
33
|
-
end
|
23
|
+
send(meth) if meth =~ /^doctor_check_/
|
24
|
+
rescue DoctorCritical
|
25
|
+
# Stop running checks in this Mechanism.
|
26
|
+
success = false
|
27
|
+
break
|
28
|
+
rescue StandardError => e
|
29
|
+
success = false
|
30
|
+
print ' ✗ '.red
|
31
|
+
puts "Exception while running check: #{e.class}: #{e.message.lines.first}"
|
32
|
+
break
|
34
33
|
end
|
35
34
|
|
36
35
|
success
|
@@ -44,13 +43,13 @@ module Moonshot
|
|
44
43
|
def warning(str, additional_info = nil)
|
45
44
|
print ' ? '.yellow
|
46
45
|
puts str
|
47
|
-
additional_info
|
46
|
+
additional_info&.lines&.each { |l| puts " #{l}" }
|
48
47
|
end
|
49
48
|
|
50
49
|
def critical(str, additional_info = nil)
|
51
50
|
print ' ✗ '.red
|
52
51
|
puts str
|
53
|
-
additional_info
|
52
|
+
additional_info&.lines&.each { |l| puts " #{l}" }
|
54
53
|
raise DoctorCritical
|
55
54
|
end
|
56
55
|
end
|
@@ -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,17 +7,36 @@ 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
|
-
|
14
|
+
attr_reader :app_name, :name
|
15
|
+
|
16
|
+
class << self
|
17
|
+
def generate_name(config)
|
18
|
+
[config.app_name, config.environment_name].join('-')
|
19
|
+
end
|
20
|
+
|
21
|
+
def make_tags(config)
|
22
|
+
default_tags = [
|
23
|
+
{ key: 'moonshot_application', value: config.app_name },
|
24
|
+
{ key: 'moonshot_environment', value: config.environment_name }
|
25
|
+
]
|
26
|
+
name = generate_name(config)
|
27
|
+
|
28
|
+
if config.additional_tag
|
29
|
+
default_tags << { key: config.additional_tag, value: name }
|
30
|
+
end
|
31
|
+
|
32
|
+
default_tags + config.extra_tags
|
33
|
+
end
|
34
|
+
end
|
14
35
|
|
15
36
|
def initialize(config)
|
16
37
|
@config = config
|
17
38
|
@ilog = config.interactive_logger
|
18
|
-
@name =
|
39
|
+
@name = self.class.generate_name(@config)
|
19
40
|
|
20
41
|
yield @config if block_given?
|
21
42
|
end
|
@@ -111,7 +132,7 @@ module Moonshot
|
|
111
132
|
resource_summary = resource_summaries.find do |r|
|
112
133
|
r.logical_resource_id == logical_id
|
113
134
|
end
|
114
|
-
resource_summary
|
135
|
+
resource_summary&.physical_resource_id
|
115
136
|
end
|
116
137
|
|
117
138
|
# @return [Array<Aws::CloudFormation::Types::StackResourceSummary>]
|
@@ -152,13 +173,23 @@ module Moonshot
|
|
152
173
|
|
153
174
|
# Support the legacy file location from Moonshot 1.0.
|
154
175
|
YamlStackTemplate.new(
|
155
|
-
File.join(@config.project_root, 'cloud_formation', "#{@config.app_name}.yml")
|
176
|
+
File.join(@config.project_root, 'cloud_formation', "#{@config.app_name}.yml")
|
177
|
+
),
|
156
178
|
JsonStackTemplate.new(
|
157
|
-
File.join(@config.project_root, 'cloud_formation', "#{@config.app_name}.json")
|
179
|
+
File.join(@config.project_root, 'cloud_formation', "#{@config.app_name}.json")
|
180
|
+
)
|
158
181
|
]
|
159
182
|
|
183
|
+
# If a template file has been specified in the config, look there first.
|
184
|
+
if @config.template_file
|
185
|
+
templates.unshift YamlStackTemplate.new(@config.template_file)
|
186
|
+
templates.unshift JsonStackTemplate.new(@config.template_file)
|
187
|
+
end
|
188
|
+
|
160
189
|
template = templates.find(&:exist?)
|
190
|
+
|
161
191
|
raise 'No template found in moonshot/template.{yml,json}!' unless template
|
192
|
+
|
162
193
|
template
|
163
194
|
end
|
164
195
|
|
@@ -177,9 +208,7 @@ module Moonshot
|
|
177
208
|
end
|
178
209
|
|
179
210
|
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
|
211
|
+
raise 'The S3 bucket to store the template in is not configured.' unless @config.template_s3_bucket
|
183
212
|
|
184
213
|
s3_object_key = "#{@name}-#{Time.now.getutc.to_i}-#{File.basename(template.filename)}"
|
185
214
|
template_url = "http://#{@config.template_s3_bucket}.s3.amazonaws.com/#{s3_object_key}"
|
@@ -199,7 +228,7 @@ module Moonshot
|
|
199
228
|
def create_stack
|
200
229
|
parameters = {
|
201
230
|
stack_name: @name,
|
202
|
-
capabilities: %w
|
231
|
+
capabilities: %w[CAPABILITY_IAM CAPABILITY_NAMED_IAM],
|
203
232
|
parameters: @config.parameters.values.map(&:to_cf),
|
204
233
|
tags: make_tags
|
205
234
|
}
|
@@ -221,11 +250,12 @@ module Moonshot
|
|
221
250
|
].join('-')
|
222
251
|
|
223
252
|
parameters = {
|
224
|
-
change_set_name
|
253
|
+
change_set_name:,
|
225
254
|
description: "Moonshot update command for application '#{Moonshot.config.app_name}'",
|
226
255
|
stack_name: @name,
|
227
|
-
capabilities: %w(CAPABILITY_IAM CAPABILITY_NAMED_IAM),
|
228
|
-
parameters: @config.parameters.values.map(&:to_cf)
|
256
|
+
capabilities: %w(CAPABILITY_IAM CAPABILITY_NAMED_IAM), # rubocop:disable Layout/HashAlignment,Style/PercentLiteralDelimiters
|
257
|
+
parameters: @config.parameters.values.map(&:to_cf),
|
258
|
+
tags: make_tags
|
229
259
|
}
|
230
260
|
if @config.template_s3_bucket
|
231
261
|
parameters[:template_url] = upload_template_to_s3
|
@@ -248,52 +278,38 @@ module Moonshot
|
|
248
278
|
events.show_only_errors unless @config.show_all_stack_events
|
249
279
|
|
250
280
|
@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
|
281
|
+
cf_client.wait_until(wait_target, stack_name: stack_id) do |w|
|
282
|
+
w.delay = 10
|
283
|
+
w.max_attempts = 360 # 60 minutes.
|
284
|
+
w.before_wait do |attempt, resp|
|
285
|
+
begin
|
286
|
+
events.latest_events.each { |e| @ilog.error(format_event(e)) }
|
287
|
+
rescue Aws::CloudFormation::Errors::ValidationError
|
288
|
+
# Do nothing. The above event logging block may result in
|
289
|
+
# a ValidationError while waiting for a stack to delete.
|
273
290
|
end
|
274
|
-
|
291
|
+
if attempt == w.max_attempts - 1
|
292
|
+
s.failure "#{stack_name} was not #{past_tense_verb} after 30 minutes."
|
293
|
+
result = false
|
275
294
|
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
295
|
+
# We don't want the interactive logger to catch an exception.
|
296
|
+
throw :success
|
297
|
+
end
|
298
|
+
s.continue "Waiting for CloudFormation Stack to be successfully #{past_tense_verb}, current status '#{resp.stacks.first.stack_status}'." # rubocop:disable Layout/LineLength
|
299
|
+
end
|
280
300
|
end
|
301
|
+
|
302
|
+
s.success "#{stack_name} successfully #{past_tense_verb}." if result
|
303
|
+
rescue Aws::Waiters::Errors::FailureStateError
|
304
|
+
result = false
|
305
|
+
s.failure "#{stack_name} failed to update."
|
281
306
|
end
|
282
307
|
|
283
308
|
result
|
284
309
|
end
|
285
310
|
|
286
311
|
def make_tags
|
287
|
-
|
288
|
-
{ key: 'moonshot_application', value: @config.app_name },
|
289
|
-
{ key: 'moonshot_environment', value: @config.environment_name }
|
290
|
-
]
|
291
|
-
|
292
|
-
if @config.additional_tag
|
293
|
-
default_tags << { key: @config.additional_tag, value: @name }
|
294
|
-
end
|
295
|
-
|
296
|
-
default_tags
|
312
|
+
self.class.make_tags(@config)
|
297
313
|
end
|
298
314
|
|
299
315
|
def format_event(event)
|
@@ -320,14 +336,15 @@ module Moonshot
|
|
320
336
|
end
|
321
337
|
|
322
338
|
def doctor_check_template_against_aws
|
339
|
+
validate_params = {}
|
323
340
|
if @config.template_s3_bucket
|
324
|
-
|
341
|
+
validate_params[:template_url] = upload_template_to_s3
|
325
342
|
else
|
326
|
-
|
343
|
+
validate_params[:template_body] = template.body
|
327
344
|
end
|
328
|
-
cf_client.validate_template(
|
345
|
+
cf_client.validate_template(validate_params)
|
329
346
|
success('CloudFormation template is valid.')
|
330
|
-
rescue => e
|
347
|
+
rescue StandardError => e
|
331
348
|
critical('Invalid CloudFormation template!', e.message)
|
332
349
|
end
|
333
350
|
|
@@ -351,6 +368,7 @@ module Moonshot
|
|
351
368
|
|
352
369
|
success = wait_for_stack_state(:stack_update_complete, 'updated')
|
353
370
|
raise 'Failed to update the CloudFormation Stack.' unless success
|
371
|
+
|
354
372
|
success
|
355
373
|
end
|
356
374
|
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
|