moonshot 0.7.7 → 1.0.0.rc1
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 +4 -4
- data/bin/moonshot +11 -0
- data/lib/default/Moonfile.rb +0 -0
- data/lib/moonshot.rb +33 -6
- data/lib/moonshot/always_use_default_source.rb +17 -0
- data/lib/moonshot/artifact_repository/s3_bucket_via_github_releases.rb +140 -3
- data/lib/moonshot/ask_user_source.rb +38 -0
- data/lib/moonshot/build_mechanism/github_release.rb +1 -1
- data/lib/moonshot/build_mechanism/script.rb +1 -1
- data/lib/moonshot/command.rb +64 -0
- data/lib/moonshot/command_line.rb +150 -0
- data/lib/moonshot/commands/build.rb +12 -0
- data/lib/moonshot/commands/console.rb +19 -0
- data/lib/moonshot/commands/create.rb +37 -0
- data/lib/moonshot/commands/delete.rb +12 -0
- data/lib/moonshot/commands/deploy.rb +12 -0
- data/lib/moonshot/commands/doctor.rb +12 -0
- data/lib/moonshot/commands/list.rb +16 -0
- data/lib/moonshot/commands/new.rb +99 -0
- data/lib/moonshot/commands/parameter_arguments.rb +27 -0
- data/lib/moonshot/commands/push.rb +12 -0
- data/lib/moonshot/commands/ssh.rb +12 -0
- data/lib/moonshot/commands/status.rb +12 -0
- data/lib/moonshot/commands/update.rb +29 -0
- data/lib/moonshot/commands/version.rb +12 -0
- data/lib/moonshot/config.rb +0 -0
- data/lib/moonshot/controller.rb +106 -42
- data/lib/moonshot/controller_config.rb +31 -13
- data/lib/moonshot/deployment_mechanism/code_deploy.rb +17 -7
- data/lib/moonshot/json_stack_template.rb +17 -0
- data/lib/moonshot/parameter_collection.rb +50 -0
- data/lib/moonshot/parent_stack_parameter_loader.rb +51 -0
- data/lib/moonshot/resources.rb +3 -3
- data/lib/moonshot/resources_helper.rb +2 -2
- data/lib/moonshot/ssh_command.rb +31 -0
- data/lib/moonshot/ssh_config.rb +1 -1
- data/lib/moonshot/stack.rb +66 -77
- data/lib/moonshot/stack_list_printer.rb +21 -0
- data/lib/moonshot/stack_lister.rb +16 -6
- data/lib/moonshot/stack_parameter.rb +64 -0
- data/lib/moonshot/stack_parameter_printer.rb +3 -49
- data/lib/moonshot/stack_template.rb +13 -25
- data/lib/moonshot/task.rb +10 -0
- data/lib/moonshot/tools/asg_rollout.rb +1 -1
- data/lib/moonshot/tools/asg_rollout/instance_health.rb +1 -1
- data/lib/moonshot/tools/asg_rollout_config.rb +1 -1
- data/lib/moonshot/yaml_stack_template.rb +17 -0
- metadata +51 -9
- data/lib/moonshot/cli.rb +0 -220
- data/lib/moonshot/environment_parser.rb +0 -32
@@ -0,0 +1,12 @@
|
|
1
|
+
module Moonshot
|
2
|
+
module Commands
|
3
|
+
class Build < Moonshot::Command
|
4
|
+
self.usage = 'build VERSION'
|
5
|
+
self.description = 'Build a release artifact, ready for deployment'
|
6
|
+
|
7
|
+
def execute(version_name)
|
8
|
+
controller.build_version(version_name)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Moonshot
|
2
|
+
module Commands
|
3
|
+
class Console < Moonshot::Command
|
4
|
+
self.usage = 'console [options]'
|
5
|
+
self.description = 'Launch a interactive Ruby console with configured access to AWS'
|
6
|
+
|
7
|
+
def execute
|
8
|
+
controller
|
9
|
+
|
10
|
+
ec2 = Aws::EC2::Client.new
|
11
|
+
iam = Aws::IAM::Client.new
|
12
|
+
autoscaling = Aws::AutoScaling::Client.new
|
13
|
+
cf = Aws::CloudFormation::Client.new
|
14
|
+
|
15
|
+
Pry.start binding, backtrace: nil
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require_relative 'parameter_arguments'
|
2
|
+
|
3
|
+
module Moonshot
|
4
|
+
module Commands
|
5
|
+
class Create < Moonshot::Command
|
6
|
+
include ParameterArguments
|
7
|
+
|
8
|
+
self.usage = 'create [options]'
|
9
|
+
self.description = 'Create a new environment'
|
10
|
+
|
11
|
+
attr_reader :version, :deploy
|
12
|
+
|
13
|
+
def parser
|
14
|
+
@deploy = true
|
15
|
+
|
16
|
+
parser = super
|
17
|
+
parser.on('-d', '--[no-]deploy', TrueClass, 'Choose if code should be deployed immediately after the stack is created') do |v| # rubocop:disable LineLength
|
18
|
+
@deploy = v
|
19
|
+
end
|
20
|
+
|
21
|
+
parser.on('--version VERSION_NAME', 'Version for initial deployment. If unset, a new development build is created from the local directory') do |v| # rubocop:disable LineLength
|
22
|
+
@version = v
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def execute
|
27
|
+
controller.create
|
28
|
+
|
29
|
+
if @deploy && @version.nil?
|
30
|
+
controller.push
|
31
|
+
elsif @deploy
|
32
|
+
controller.deploy_version(@version)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Moonshot
|
2
|
+
module Commands
|
3
|
+
class Deploy < Moonshot::Command
|
4
|
+
self.usage = 'deploy VERSION'
|
5
|
+
self.description = 'Deploy a versioned release to the environment'
|
6
|
+
|
7
|
+
def execute(version_name)
|
8
|
+
controller.deploy_version(version_name)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Moonshot
|
2
|
+
module Commands
|
3
|
+
class Doctor < Moonshot::Command
|
4
|
+
self.usage = 'doctor [options]'
|
5
|
+
self.description = 'Run configuration checks against the local environment'
|
6
|
+
|
7
|
+
def execute
|
8
|
+
controller.doctor || raise('One or more checks failed.')
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require_relative '../stack_lister'
|
2
|
+
require_relative '../stack_list_printer'
|
3
|
+
|
4
|
+
module Moonshot
|
5
|
+
module Commands
|
6
|
+
class List < Moonshot::Command
|
7
|
+
self.usage = 'list [options]'
|
8
|
+
self.description = 'List stacks for this application'
|
9
|
+
|
10
|
+
def execute
|
11
|
+
stacks = StackLister.new(controller.config.app_name).list
|
12
|
+
StackListPrinter.new(stacks).print
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module Moonshot
|
2
|
+
module Commands
|
3
|
+
class New < Moonshot::Command
|
4
|
+
self.usage = 'new [options]'
|
5
|
+
self.description = 'Creates a new Moonshot project.'
|
6
|
+
|
7
|
+
DEFAULT_DIRECTORY = File.join(__dir__, '..', '..', 'default').freeze
|
8
|
+
|
9
|
+
def execute
|
10
|
+
warn 'Looks like your project is already set up!'
|
11
|
+
end
|
12
|
+
|
13
|
+
class << self
|
14
|
+
def run!(application_name)
|
15
|
+
@application_name = application_name
|
16
|
+
|
17
|
+
create_project_dir
|
18
|
+
copy_defaults
|
19
|
+
create_file(parameter_path)
|
20
|
+
create_file(template_path)
|
21
|
+
fill_moonfile
|
22
|
+
print_success_message
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def cwd
|
28
|
+
Dir.pwd
|
29
|
+
end
|
30
|
+
|
31
|
+
def create_project_dir
|
32
|
+
raise "Directory '#{@application_name}' already exists!" \
|
33
|
+
if Dir.exist?(project_path)
|
34
|
+
Dir.mkdir(project_path)
|
35
|
+
end
|
36
|
+
|
37
|
+
def project_path
|
38
|
+
@project_path ||= File.join(cwd, @application_name)
|
39
|
+
end
|
40
|
+
|
41
|
+
def copy_defaults
|
42
|
+
target_path = File.join(DEFAULT_DIRECTORY.dup, '.')
|
43
|
+
FileUtils.cp_r(target_path, project_path)
|
44
|
+
end
|
45
|
+
|
46
|
+
def create_file(path)
|
47
|
+
FileUtils.touch(path)
|
48
|
+
end
|
49
|
+
|
50
|
+
def moonfile_path
|
51
|
+
File.join(project_path, 'Moonfile.rb')
|
52
|
+
end
|
53
|
+
|
54
|
+
def parameter_path
|
55
|
+
File.join(cf_dir, 'parameters', "#{@application_name}.yml")
|
56
|
+
end
|
57
|
+
|
58
|
+
def template_path
|
59
|
+
File.join(cf_dir, "#{@application_name}.json")
|
60
|
+
end
|
61
|
+
|
62
|
+
def cf_dir
|
63
|
+
File.join(project_path, 'cloud_formation')
|
64
|
+
end
|
65
|
+
|
66
|
+
def fill_moonfile
|
67
|
+
File.open(moonfile_path, 'w') { |f| f.write generate_moonfile }
|
68
|
+
end
|
69
|
+
|
70
|
+
def generate_moonfile
|
71
|
+
<<-EOF
|
72
|
+
Moonshot.config do |m|
|
73
|
+
m.app_name = '#{@application_name}'
|
74
|
+
m.artifact_repository = S3Bucket.new('<your_bucket>')
|
75
|
+
m.build_mechanism = Script.new('bin/build.sh')
|
76
|
+
m.deployment_mechanism = CodeDeploy.new(asg: 'AutoScalingGroup')
|
77
|
+
end
|
78
|
+
EOF
|
79
|
+
end
|
80
|
+
|
81
|
+
def print_success_message
|
82
|
+
warn 'Your application is configured, the following changes have '\
|
83
|
+
'been made to your project directory:'
|
84
|
+
warn ''
|
85
|
+
warn '- Created a Moonfile.rb where you can configure your project.'
|
86
|
+
warn '- Created moonshot/plugins where you can add hooks to core '\
|
87
|
+
'Moonshot actions.'
|
88
|
+
warn '- Created moonshot/cli_extensions where you can create '\
|
89
|
+
'project-specific commands.'
|
90
|
+
warn ''
|
91
|
+
warn 'You will also need to ensure your Amazon account is configured'\
|
92
|
+
' for CodeDeploy, by creating a role that allows deployments. '\
|
93
|
+
'See: http://moonshot.readthedocs.io/en/latest/mechanisms/'\
|
94
|
+
'deployment/'
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# rubocop:disable LineLength
|
2
|
+
module Moonshot
|
3
|
+
module Commands
|
4
|
+
module ParameterArguments
|
5
|
+
def parser
|
6
|
+
parser = super
|
7
|
+
|
8
|
+
parser.on('--[no-]interactive', TrueClass, 'Use interactive prompts for gathering missing configuration.') do |v|
|
9
|
+
Moonshot.config.interactive = v
|
10
|
+
end
|
11
|
+
|
12
|
+
parser.on('--answer-file FILE', '-aFILE', 'Load Stack Parameters from a YAML file') do |v|
|
13
|
+
Moonshot.config.answer_file = File.expand_path(v)
|
14
|
+
end
|
15
|
+
|
16
|
+
parser.on('--parameter KEY=VALUE', '-PKEY=VALUE', 'Specify Stack Parameter on the command line') do |v|
|
17
|
+
data = v.split('=', 2)
|
18
|
+
unless data.size == 2
|
19
|
+
raise "Invalid parameter format '#{v}', expected KEY=VALUE (e.g. MyStackParameter=12)"
|
20
|
+
end
|
21
|
+
|
22
|
+
Moonshot.config.parameter_overrides[data[0]] = data[1]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require_relative 'parameter_arguments'
|
2
|
+
|
3
|
+
module Moonshot
|
4
|
+
module Commands
|
5
|
+
class Update < Moonshot::Command
|
6
|
+
include ParameterArguments
|
7
|
+
|
8
|
+
self.usage = 'update [options]'
|
9
|
+
self.description = 'Update the CloudFormation stack within an environment.'
|
10
|
+
|
11
|
+
def execute
|
12
|
+
controller.update
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def parameter_strategy_factory(value)
|
18
|
+
case value.to_sym
|
19
|
+
when :default
|
20
|
+
Moonshot::ParameterStrategy::DefaultStrategy.new
|
21
|
+
when :merge
|
22
|
+
Moonshot::ParameterStrategy::MergeStrategy.new
|
23
|
+
else
|
24
|
+
raise "Unknown parameter strategy: #{value}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
File without changes
|
data/lib/moonshot/controller.rb
CHANGED
@@ -1,10 +1,14 @@
|
|
1
1
|
require_relative 'ssh_target_selector'
|
2
2
|
require_relative 'ssh_command_builder'
|
3
3
|
|
4
|
+
require_relative 'stack_parameter'
|
5
|
+
require_relative 'parameter_collection'
|
6
|
+
require_relative 'parent_stack_parameter_loader'
|
7
|
+
|
4
8
|
module Moonshot
|
5
9
|
# The Controller coordinates and performs all Moonshot actions.
|
6
10
|
class Controller # rubocop:disable ClassLength
|
7
|
-
|
11
|
+
attr_accessor :config
|
8
12
|
|
9
13
|
def initialize
|
10
14
|
@config = ControllerConfig.new
|
@@ -12,22 +16,108 @@ module Moonshot
|
|
12
16
|
end
|
13
17
|
|
14
18
|
def list
|
15
|
-
Moonshot::StackLister.new(
|
16
|
-
@config.app_name, log: @config.logger).list
|
19
|
+
Moonshot::StackLister.new(@config.app_name).list
|
17
20
|
end
|
18
21
|
|
19
|
-
def create
|
22
|
+
def create # rubocop:disable AbcSize
|
23
|
+
# Scan the template for all required parameters and configure
|
24
|
+
# the ParameterCollection.
|
25
|
+
@config.parameters = ParameterCollection.from_template(stack.template)
|
26
|
+
|
27
|
+
# Import all Outputs from parent stacks as Parameters on this
|
28
|
+
# stack.
|
29
|
+
ParentStackParameterLoader.new(@config).load!
|
30
|
+
|
31
|
+
# If there is an answer file, use it to populate parameters.
|
32
|
+
if @config.answer_file
|
33
|
+
YAML.load_file(@config.answer_file).each do |key, value|
|
34
|
+
@config.parameters[key] = value
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Apply any overrides configured, such as from the CLI -p option.
|
39
|
+
@config.parameter_overrides.each do |key, value|
|
40
|
+
@config.parameters[key] = value
|
41
|
+
end
|
42
|
+
|
43
|
+
# Interview the user for missing parameters, using the
|
44
|
+
# appropriate prompts.
|
45
|
+
@config.parameters.values.each do |sp|
|
46
|
+
next if sp.set?
|
47
|
+
|
48
|
+
parameter_source = @config.parameter_sources.fetch(sp.name,
|
49
|
+
@config.default_parameter_source)
|
50
|
+
parameter_source.get(sp)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Plugins get the final say on parameters before create,
|
54
|
+
# allowing them to manipulate user supplied input and answers
|
55
|
+
# file content.
|
20
56
|
run_plugins(:pre_create)
|
57
|
+
|
58
|
+
# Fail if any parameters are still missing without defaults.
|
59
|
+
missing_parameters = @config.parameters.missing_for_create
|
60
|
+
unless missing_parameters.empty?
|
61
|
+
raise "The following parameters were not provided: #{missing_parameters.map(&:name).join(', ')}" # rubocop:disable LineLength
|
62
|
+
end
|
63
|
+
|
21
64
|
run_hook(:deploy, :pre_create)
|
22
65
|
stack_ok = stack.create
|
23
66
|
if stack_ok # rubocop:disable GuardClause
|
24
67
|
run_hook(:deploy, :post_create)
|
25
68
|
run_plugins(:post_create)
|
69
|
+
else
|
70
|
+
raise 'Stack creation failed!'
|
26
71
|
end
|
27
72
|
end
|
28
73
|
|
29
|
-
def update
|
74
|
+
def update # rubocop:disable AbcSize
|
75
|
+
# Scan the template for all required parameters and configure
|
76
|
+
# the ParameterCollection.
|
77
|
+
@config.parameters = ParameterCollection.from_template(stack.template)
|
78
|
+
|
79
|
+
# Set all values already provided by the stack to UsePreviousValue.
|
80
|
+
stack.parameters.each do |key, value|
|
81
|
+
@config.parameters[key].use_previous!(value) if @config.parameters.key?(key)
|
82
|
+
end
|
83
|
+
|
84
|
+
# Import all Outputs from parent stacks as Parameters on this
|
85
|
+
# stack.
|
86
|
+
ParentStackParameterLoader.new(@config).load_missing_only!
|
87
|
+
|
88
|
+
# If there is an answer file, use it to populate parameters.
|
89
|
+
if @config.answer_file
|
90
|
+
YAML.load_file(@config.answer_file).each do |key, value|
|
91
|
+
@config.parameters[key] = value
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Apply any overrides configured, such as from the CLI -p option.
|
96
|
+
@config.parameter_overrides.each do |key, value|
|
97
|
+
@config.parameters[key] = value
|
98
|
+
end
|
99
|
+
|
100
|
+
# Interview the user for missing parameters, using the
|
101
|
+
# appropriate prompts.
|
102
|
+
@config.parameters.values.each do |sp|
|
103
|
+
next if sp.set?
|
104
|
+
|
105
|
+
parameter_source = @config.parameter_sources.fetch(sp.name,
|
106
|
+
@config.default_parameter_source)
|
107
|
+
parameter_source.get(sp)
|
108
|
+
end
|
109
|
+
|
110
|
+
# Plugins get the final say on parameters before create,
|
111
|
+
# allowing them to manipulate user supplied input and answers
|
112
|
+
# file content.
|
30
113
|
run_plugins(:pre_update)
|
114
|
+
|
115
|
+
# Fail if any parameters are still missing without defaults.
|
116
|
+
missing_parameters = @config.parameters.missing_for_update
|
117
|
+
unless missing_parameters.empty?
|
118
|
+
raise "The following parameters were not provided: #{missing_parameters.map(&:name).join(', ')}" # rubocop:disable LineLength
|
119
|
+
end
|
120
|
+
|
31
121
|
run_hook(:deploy, :pre_update)
|
32
122
|
stack.update
|
33
123
|
run_hook(:deploy, :post_update)
|
@@ -41,8 +131,8 @@ module Moonshot
|
|
41
131
|
run_plugins(:post_status)
|
42
132
|
end
|
43
133
|
|
44
|
-
def
|
45
|
-
version =
|
134
|
+
def push
|
135
|
+
version = @config.dev_build_name_proc.call(@config)
|
46
136
|
build_version(version)
|
47
137
|
deploy_version(version)
|
48
138
|
end
|
@@ -63,6 +153,12 @@ module Moonshot
|
|
63
153
|
end
|
64
154
|
|
65
155
|
def delete
|
156
|
+
# Populate the current values of parameters, for use by plugins.
|
157
|
+
@config.parameters = ParameterCollection.from_template(stack.template)
|
158
|
+
stack.parameters.each do |key, value|
|
159
|
+
@config.parameters[key].use_previous!(value) if @config.parameters.key?(key)
|
160
|
+
end
|
161
|
+
|
66
162
|
run_plugins(:pre_delete)
|
67
163
|
run_hook(:deploy, :pre_delete)
|
68
164
|
stack.delete
|
@@ -71,7 +167,6 @@ module Moonshot
|
|
71
167
|
end
|
72
168
|
|
73
169
|
def doctor
|
74
|
-
# @todo use #run_hook when Stack becomes an InfrastructureProvider
|
75
170
|
success = true
|
76
171
|
success &&= stack.doctor_hook
|
77
172
|
success &&= run_hook(:build, :doctor)
|
@@ -90,56 +185,25 @@ module Moonshot
|
|
90
185
|
cb = SSHCommandBuilder.new(@config.ssh_config, @config.ssh_instance)
|
91
186
|
result = cb.build(@config.ssh_command)
|
92
187
|
|
93
|
-
|
188
|
+
warn "Opening SSH connection to #{@config.ssh_instance} (#{result.ip})..."
|
94
189
|
exec(result.cmd)
|
95
190
|
end
|
96
191
|
|
97
192
|
def stack
|
98
|
-
@stack ||= Stack.new(
|
99
|
-
app_name: @config.app_name,
|
100
|
-
log: @config.logger,
|
101
|
-
ilog: @config.interactive_logger) do |config|
|
102
|
-
config.parent_stacks = @config.parent_stacks
|
103
|
-
config.show_all_events = @config.show_all_stack_events
|
104
|
-
config.parameter_strategy = @config.parameter_strategy
|
105
|
-
end
|
193
|
+
@stack ||= Stack.new(@config)
|
106
194
|
end
|
107
195
|
|
108
196
|
private
|
109
197
|
|
110
|
-
def default_stack_name
|
111
|
-
user = ENV.fetch('USER').gsub(/\W/, '')
|
112
|
-
"#{@config.app_name}-dev-#{user}"
|
113
|
-
end
|
114
|
-
|
115
|
-
def ensure_prefix(name)
|
116
|
-
if name.start_with?(@config.app_name + '-')
|
117
|
-
name
|
118
|
-
else
|
119
|
-
@config.app_name + "-#{name}"
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
def stack_name
|
124
|
-
name = @config.environment_name || default_stack_name
|
125
|
-
if @config.auto_prefix_stack == false
|
126
|
-
name
|
127
|
-
else
|
128
|
-
ensure_prefix(name)
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
198
|
def resources
|
133
199
|
@resources ||=
|
134
|
-
Resources.new(stack: stack,
|
135
|
-
ilog: @config.interactive_logger)
|
200
|
+
Resources.new(stack: stack, ilog: @config.interactive_logger, controller: self)
|
136
201
|
end
|
137
202
|
|
138
203
|
def run_hook(type, name, *args)
|
139
204
|
mech = get_mechanism(type)
|
140
205
|
name = name.to_s << '_hook'
|
141
206
|
|
142
|
-
@config.logger.debug("Calling hook=#{name} on mech=#{mech.class}")
|
143
207
|
return unless mech && mech.respond_to?(name)
|
144
208
|
|
145
209
|
mech.resources = resources
|