moonshot 0.7.7 → 1.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|