moonshot 1.0.0 → 1.1.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/moonshot.rb +3 -52
- data/lib/moonshot/account_context.rb +19 -0
- data/lib/moonshot/change_set.rb +100 -0
- data/lib/moonshot/command.rb +4 -14
- data/lib/moonshot/command_line.rb +3 -12
- data/lib/moonshot/command_line_dispatcher.rb +71 -0
- data/lib/moonshot/commands/create.rb +2 -2
- data/lib/moonshot/commands/delete.rb +2 -0
- data/lib/moonshot/commands/list.rb +0 -3
- data/lib/moonshot/commands/new.rb +26 -48
- data/lib/moonshot/commands/parent_stack_option.rb +14 -0
- data/lib/moonshot/commands/show_all_events_option.rb +13 -0
- data/lib/moonshot/commands/update.rb +14 -14
- data/lib/moonshot/controller.rb +4 -12
- data/lib/moonshot/controller_config.rb +18 -5
- data/lib/moonshot/deployment_mechanism/code_deploy.rb +24 -7
- data/lib/moonshot/json_stack_template.rb +0 -1
- data/lib/moonshot/stack.rb +62 -80
- data/lib/moonshot/stack_config.rb +0 -1
- data/lib/moonshot/stack_template.rb +2 -2
- data/lib/moonshot/tools/asg_rollout.rb +0 -4
- data/lib/moonshot/tools/asg_rollout/asg.rb +0 -3
- data/lib/moonshot/yaml_stack_template.rb +0 -1
- data/lib/plugins/backup.rb +0 -1
- metadata +23 -6
- data/lib/moonshot/default_strategy.rb +0 -16
- data/lib/moonshot/merge_strategy.rb +0 -31
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2940b0c43a48274eb8368400726fc3269ff6fdf9
|
4
|
+
data.tar.gz: fb6dfbf21127e8c55533bf848361f81c07f2b0d7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 146242a237abcccc8229257e0c73310ff539ba1483a57695cc1d6ccbc534778ac015354a9b91fa7ee09e83a8c2e921c3e61946dfd7a5ce0169b04ef005b37376
|
7
|
+
data.tar.gz: aaf546412e380adc74bc823e7e2a7aef2e1ae31eafb35c2fe284e075b714940eb3d88b7c72dfa53a890f1bf9a62b0fb2108691f52eca510aa205bc1eccf67af2
|
data/lib/moonshot.rb
CHANGED
@@ -24,55 +24,6 @@ module Moonshot
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
'doctor_helper',
|
31
|
-
'resources',
|
32
|
-
'resources_helper',
|
33
|
-
|
34
|
-
# Core
|
35
|
-
'interactive_logger_proxy',
|
36
|
-
'command_line',
|
37
|
-
'command',
|
38
|
-
'ssh_command',
|
39
|
-
'commands/build',
|
40
|
-
'commands/console',
|
41
|
-
'commands/create',
|
42
|
-
'commands/delete',
|
43
|
-
'commands/deploy',
|
44
|
-
'commands/doctor',
|
45
|
-
'commands/list',
|
46
|
-
'commands/push',
|
47
|
-
'commands/ssh',
|
48
|
-
'commands/status',
|
49
|
-
'commands/update',
|
50
|
-
'commands/version',
|
51
|
-
'controller',
|
52
|
-
'controller_config',
|
53
|
-
'stack',
|
54
|
-
'stack_config',
|
55
|
-
'stack_lister',
|
56
|
-
'stack_events_poller',
|
57
|
-
'merge_strategy',
|
58
|
-
'default_strategy',
|
59
|
-
'ask_user_source',
|
60
|
-
'always_use_default_source',
|
61
|
-
|
62
|
-
# Built-in mechanisms
|
63
|
-
'artifact_repository/s3_bucket',
|
64
|
-
'artifact_repository/s3_bucket_via_github_releases',
|
65
|
-
'build_mechanism/script',
|
66
|
-
'build_mechanism/github_release',
|
67
|
-
'build_mechanism/travis_deploy',
|
68
|
-
'build_mechanism/version_proxy',
|
69
|
-
'deployment_mechanism/code_deploy',
|
70
|
-
|
71
|
-
# Core Tools
|
72
|
-
'tools/asg_rollout'
|
73
|
-
].each { |f| require_relative "moonshot/#{f}" }
|
74
|
-
|
75
|
-
# Bundled plugins
|
76
|
-
[
|
77
|
-
'backup'
|
78
|
-
].each { |p| require_relative "plugins/#{p}" }
|
27
|
+
require 'require_all'
|
28
|
+
require_rel 'moonshot'
|
29
|
+
require_rel 'plugins'
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Moonshot
|
2
|
+
module AccountContext
|
3
|
+
def self.get
|
4
|
+
@account ||= determine_account_name
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.set(account_name)
|
8
|
+
@account = account_name
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.reset
|
12
|
+
@account = nil
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.determine_account_name
|
16
|
+
Aws::IAM::Client.new.list_account_aliases.account_aliases.first
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
module Moonshot
|
2
|
+
class ChangeSet
|
3
|
+
attr_reader :name
|
4
|
+
attr_reader :stack_name
|
5
|
+
|
6
|
+
def initialize(name, stack_name)
|
7
|
+
@name = name
|
8
|
+
@stack_name = stack_name
|
9
|
+
@change_set = nil
|
10
|
+
@cf_client = Aws::CloudFormation::Client.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def confirm?
|
14
|
+
unless Moonshot.config.interactive
|
15
|
+
raise 'Cannot confirm ChangeSet when interactive mode is disabled!'
|
16
|
+
end
|
17
|
+
|
18
|
+
loop do
|
19
|
+
print 'Apply changes? '
|
20
|
+
resp = gets.chomp.downcase
|
21
|
+
|
22
|
+
return true if resp == 'yes'
|
23
|
+
return false if resp == 'no'
|
24
|
+
puts "Please enter 'yes' or 'no'!"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def valid?
|
29
|
+
@change_set.status == 'CREATE_COMPLETE'
|
30
|
+
end
|
31
|
+
|
32
|
+
def invalid_reason
|
33
|
+
@change_set.status_reason
|
34
|
+
end
|
35
|
+
|
36
|
+
def display_changes
|
37
|
+
wait_for_change_set unless @change_set
|
38
|
+
|
39
|
+
@change_set.changes.map(&:resource_change).each do |c|
|
40
|
+
puts "* #{c.action} #{c.logical_resource_id} (#{c.resource_type})"
|
41
|
+
|
42
|
+
if c.replacement == 'True'
|
43
|
+
puts ' - Will be replaced'
|
44
|
+
elsif c.replacement == 'Conditional'
|
45
|
+
puts ' - May be replaced (Conditional)'
|
46
|
+
end
|
47
|
+
|
48
|
+
c.details.each do |d|
|
49
|
+
case d.change_source
|
50
|
+
when 'ResourceReference', 'ParameterReference'
|
51
|
+
puts " - Caused by #{d.causing_entity.blue} (#{d.change_source})"
|
52
|
+
when 'DirectModification'
|
53
|
+
puts " - Caused by template change (#{d.target.attribute}: #{d.target.name})"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def execute
|
60
|
+
wait_for_change_set unless @change_set
|
61
|
+
@cf_client.execute_change_set(
|
62
|
+
change_set_name: @name,
|
63
|
+
stack_name: @stack_name)
|
64
|
+
end
|
65
|
+
|
66
|
+
def delete
|
67
|
+
wait_for_change_set unless @change_set
|
68
|
+
@cf_client.delete_change_set(
|
69
|
+
change_set_name: @name,
|
70
|
+
stack_name: @stack_name)
|
71
|
+
rescue Aws::CloudFormation::Errors::InvalidChangeSetStatus
|
72
|
+
sleep 1
|
73
|
+
retry
|
74
|
+
end
|
75
|
+
|
76
|
+
# NOTE: At the time of this patch, AWS-SDK native Waiters do not
|
77
|
+
# have support for ChangeSets. Once they add this, we can make
|
78
|
+
# this code much better.
|
79
|
+
def wait_for_change_set
|
80
|
+
start = Time.now.to_i
|
81
|
+
|
82
|
+
loop do
|
83
|
+
resp = @cf_client.describe_change_set(
|
84
|
+
change_set_name: @name,
|
85
|
+
stack_name: @stack_name)
|
86
|
+
|
87
|
+
if %w(CREATE_COMPLETE FAILED).include?(resp.status)
|
88
|
+
@change_set = resp
|
89
|
+
return
|
90
|
+
end
|
91
|
+
|
92
|
+
if Time.now.to_i > start + 30
|
93
|
+
raise 'ChangeSet did not complete creation within 30 seconds!'
|
94
|
+
end
|
95
|
+
|
96
|
+
sleep 0.25 # http://bit.ly/1qY1ZXJ
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
data/lib/moonshot/command.rb
CHANGED
@@ -5,7 +5,7 @@ module Moonshot
|
|
5
5
|
class Command
|
6
6
|
module ClassMethods
|
7
7
|
# TODO: Can we auto-generate usage for commands with no positional arguments, at least?
|
8
|
-
attr_accessor :usage, :description
|
8
|
+
attr_accessor :usage, :description, :only_in_account
|
9
9
|
end
|
10
10
|
|
11
11
|
def self.inherited(base)
|
@@ -30,15 +30,6 @@ module Moonshot
|
|
30
30
|
o.on('--[no-]interactive-logger', TrueClass, 'Enable or disable fancy logging') do |v|
|
31
31
|
@use_interactive_logger = v
|
32
32
|
end
|
33
|
-
|
34
|
-
o.on('--[no-]show-all-events', FalseClass, 'Show all stack events during update') do |v|
|
35
|
-
Moonshot.config.show_all_stack_events = v
|
36
|
-
end
|
37
|
-
|
38
|
-
o.on('-pPARENT_STACK', '--parent=PARENT_STACK',
|
39
|
-
'Parent stack to import parameters from') do |v|
|
40
|
-
Moonshot.config.parent_stacks = [v]
|
41
|
-
end
|
42
33
|
end
|
43
34
|
end
|
44
35
|
|
@@ -46,10 +37,9 @@ module Moonshot
|
|
46
37
|
|
47
38
|
# Build a Moonshot::Controller from the CLI options.
|
48
39
|
def controller
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
controller.config = Moonshot.config
|
40
|
+
config = Moonshot.config
|
41
|
+
config.update_for_account!
|
42
|
+
controller = Moonshot::Controller.new(config)
|
53
43
|
|
54
44
|
# Degrade to a more compatible logger if the terminal seems outdated,
|
55
45
|
# or at the users request.
|
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'thor'
|
2
|
-
|
3
1
|
module Moonshot
|
4
2
|
# This class implements the command-line `moonshot` tool.
|
5
3
|
class CommandLine
|
@@ -12,7 +10,7 @@ module Moonshot
|
|
12
10
|
@classes || []
|
13
11
|
end
|
14
12
|
|
15
|
-
def run! # rubocop:disable
|
13
|
+
def run! # rubocop:disable CyclomaticComplexity, MethodLength, PerceivedComplexity
|
16
14
|
# Commands defined as Moonshot::Commands require a properly
|
17
15
|
# configured Moonshot.rb and supporting files. Without them, we only
|
18
16
|
# support `--help` and `new`.
|
@@ -65,15 +63,9 @@ module Moonshot
|
|
65
63
|
raise "Command not found '#{command}'"
|
66
64
|
end
|
67
65
|
|
68
|
-
|
69
|
-
handler.parser.parse!
|
70
|
-
|
71
|
-
unless ARGV.size == handler.method(:execute).arity
|
72
|
-
warn handler.parser.help
|
73
|
-
raise "Invalid command line for '#{command}'."
|
74
|
-
end
|
66
|
+
command_class = @commands[command]
|
75
67
|
|
76
|
-
|
68
|
+
CommandLineDispatcher.new(command, command_class, ARGV).dispatch!
|
77
69
|
end
|
78
70
|
|
79
71
|
def load_plugins(moonfile_dir)
|
@@ -137,7 +129,6 @@ module Moonshot
|
|
137
129
|
ARGV.delete_at(0)
|
138
130
|
ARGV.push('-h')
|
139
131
|
elsif ARGV[0] == 'new'
|
140
|
-
require_relative 'commands/new'
|
141
132
|
app_name = ARGV[1]
|
142
133
|
::Moonshot::Commands::New.run!(app_name)
|
143
134
|
return true
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Moonshot
|
2
|
+
class CommandLineDispatcher
|
3
|
+
def initialize(command, klass, args)
|
4
|
+
@command = command
|
5
|
+
@klass = klass
|
6
|
+
@args = args
|
7
|
+
end
|
8
|
+
|
9
|
+
def dispatch!
|
10
|
+
# Look to see if we're allowed only to run in certain accounts, or
|
11
|
+
# not allowed to run in specific accounts.
|
12
|
+
check_account_restrictions
|
13
|
+
|
14
|
+
# Allow any mechanisms or plugins to hook into this CLI command.
|
15
|
+
handler = @klass.new
|
16
|
+
parser = build_parser(handler)
|
17
|
+
parser.parse!
|
18
|
+
|
19
|
+
unless @args.size == handler.method(:execute).arity
|
20
|
+
warn handler.parser.help
|
21
|
+
raise "Invalid command line for '#{@command}'."
|
22
|
+
end
|
23
|
+
|
24
|
+
handler.execute(*@args)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def check_account_restrictions
|
30
|
+
this_account = Moonshot::AccountContext.get
|
31
|
+
|
32
|
+
return if @klass.only_in_account.nil? ||
|
33
|
+
Array(@klass.only_in_account).any? { |a| a == this_account }
|
34
|
+
|
35
|
+
warn "'#{@command}' can only be run in the following accounts:"
|
36
|
+
Array(@klass.only_in_account).each do |account_name|
|
37
|
+
warn "- #{account_name}"
|
38
|
+
end
|
39
|
+
|
40
|
+
raise 'Command account restriction violation.'
|
41
|
+
end
|
42
|
+
|
43
|
+
def build_parser(handler)
|
44
|
+
parser = handler.parser
|
45
|
+
|
46
|
+
# Each mechanism / plugin may manipulate the OptionParser object
|
47
|
+
# associated with this command.
|
48
|
+
[:build_mechanism, :deployment_mechanism, :artifact_repository].each do |prov|
|
49
|
+
provider = Moonshot.config.send(prov)
|
50
|
+
|
51
|
+
if provider.respond_to?(hook_func_name(@command))
|
52
|
+
parser = provider.send(hook_func_name(@command), parser)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
Moonshot.config.plugins.each do |plugin|
|
57
|
+
if plugin.respond_to?(hook_func_name(@command))
|
58
|
+
parser = plugin.send(hook_func_name(@command), parser)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
parser
|
63
|
+
end
|
64
|
+
|
65
|
+
# Name of the function a plugin or mechanism could define to manipulate
|
66
|
+
# the parser for a command.
|
67
|
+
def hook_func_name(command)
|
68
|
+
command.tr('-', '_') << '_cli_hook'
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -1,9 +1,9 @@
|
|
1
|
-
require_relative 'parameter_arguments'
|
2
|
-
|
3
1
|
module Moonshot
|
4
2
|
module Commands
|
5
3
|
class Create < Moonshot::Command
|
6
4
|
include ParameterArguments
|
5
|
+
include ShowAllEventsOption
|
6
|
+
include ParentStackOption
|
7
7
|
|
8
8
|
self.usage = 'create [options]'
|
9
9
|
self.description = 'Create a new environment'
|
@@ -16,18 +16,12 @@ module Moonshot
|
|
16
16
|
|
17
17
|
create_project_dir
|
18
18
|
copy_defaults
|
19
|
-
create_file(parameter_path)
|
20
|
-
create_file(template_path)
|
21
19
|
fill_moonfile
|
22
20
|
print_success_message
|
23
21
|
end
|
24
22
|
|
25
23
|
private
|
26
24
|
|
27
|
-
def cwd
|
28
|
-
Dir.pwd
|
29
|
-
end
|
30
|
-
|
31
25
|
def create_project_dir
|
32
26
|
raise "Directory '#{@application_name}' already exists!" \
|
33
27
|
if Dir.exist?(project_path)
|
@@ -35,7 +29,7 @@ module Moonshot
|
|
35
29
|
end
|
36
30
|
|
37
31
|
def project_path
|
38
|
-
@project_path ||= File.join(
|
32
|
+
@project_path ||= File.join(Dir.pwd, @application_name)
|
39
33
|
end
|
40
34
|
|
41
35
|
def copy_defaults
|
@@ -43,55 +37,39 @@ module Moonshot
|
|
43
37
|
FileUtils.cp_r(target_path, project_path)
|
44
38
|
end
|
45
39
|
|
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
40
|
def fill_moonfile
|
67
|
-
File.open(
|
41
|
+
File.open(File.join(project_path, 'Moonfile.rb'), 'w') { |f| f.write generate_moonfile }
|
68
42
|
end
|
69
43
|
|
70
44
|
def generate_moonfile
|
71
45
|
<<-EOF
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
46
|
+
Moonshot.config do |m|
|
47
|
+
m.app_name = '#{@application_name}'
|
48
|
+
m.artifact_repository = S3Bucket.new('<your_bucket>')
|
49
|
+
m.build_mechanism = Script.new('bin/build.sh')
|
50
|
+
m.deployment_mechanism = CodeDeploy.new(asg: 'AutoScalingGroup')
|
51
|
+
end
|
52
|
+
EOF
|
79
53
|
end
|
80
54
|
|
81
55
|
def print_success_message
|
82
|
-
warn
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
56
|
+
warn <<-EOF
|
57
|
+
Your application is configured, the following changes have been made
|
58
|
+
to your project directory:
|
59
|
+
|
60
|
+
* Created Moonfile.rb, where you can configure your project.
|
61
|
+
* Created moonshot/plugins, where you can place custom Ruby code
|
62
|
+
to add hooks to core Moonshot actions (create, update, delete, etc.)
|
63
|
+
* Created moonshot/cli_extensions, where you can place custom Ruby
|
64
|
+
code to add your own project-specific commands to Moonshot.
|
65
|
+
* Created moonshot/template.yml, where you can build your
|
66
|
+
CloudFormation template.
|
67
|
+
|
68
|
+
You will also need to ensure your Amazon account is configured for
|
69
|
+
CodeDeploy by creating a role that allows deployments.
|
70
|
+
|
71
|
+
See: http://moonshot.readthedocs.io/en/latest/mechanisms/deployment/
|
72
|
+
EOF
|
95
73
|
end
|
96
74
|
end
|
97
75
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Moonshot
|
2
|
+
module Commands
|
3
|
+
module ParentStackOption
|
4
|
+
def parser
|
5
|
+
parser = super
|
6
|
+
|
7
|
+
parser.on('-pPARENT_STACK', '--parent=PARENT_STACK',
|
8
|
+
'Parent stack to import parameters from') do |v|
|
9
|
+
Moonshot.config.parent_stacks = [v]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Moonshot
|
2
|
+
module Commands
|
3
|
+
module ShowAllEventsOption
|
4
|
+
def parser
|
5
|
+
parser = super
|
6
|
+
|
7
|
+
parser.on('--[no-]show-all-events', TrueClass, 'Show all stack events during update') do |v|
|
8
|
+
Moonshot.config.show_all_stack_events = v
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -1,29 +1,29 @@
|
|
1
|
-
require_relative 'parameter_arguments'
|
2
|
-
|
3
1
|
module Moonshot
|
4
2
|
module Commands
|
5
3
|
class Update < Moonshot::Command
|
6
4
|
include ParameterArguments
|
5
|
+
include ShowAllEventsOption
|
6
|
+
include ParentStackOption
|
7
7
|
|
8
8
|
self.usage = 'update [options]'
|
9
9
|
self.description = 'Update the CloudFormation stack within an environment.'
|
10
10
|
|
11
|
-
def
|
12
|
-
|
13
|
-
end
|
11
|
+
def parser
|
12
|
+
parser = super
|
14
13
|
|
15
|
-
|
14
|
+
parser.on('--dry-run', TrueClass, 'Show the changes that would be applied, but do not execute them') do |v| # rubocop:disable LineLength
|
15
|
+
@dry_run = v
|
16
|
+
end
|
16
17
|
|
17
|
-
|
18
|
-
|
19
|
-
when :default
|
20
|
-
Moonshot::ParameterStrategy::DefaultStrategy.new
|
21
|
-
when :merge
|
22
|
-
Moonshot::ParameterStrategy::MergeStrategy.new
|
23
|
-
else
|
24
|
-
raise "Unknown parameter strategy: #{value}"
|
18
|
+
parser.on('--force', '-f', TrueClass, 'Apply ChangeSet without confirmation') do |v|
|
19
|
+
@force = v
|
25
20
|
end
|
26
21
|
end
|
22
|
+
|
23
|
+
def execute
|
24
|
+
@force = true unless Moonshot.config.interactive
|
25
|
+
controller.update(dry_run: @dry_run, force: @force)
|
26
|
+
end
|
27
27
|
end
|
28
28
|
end
|
29
29
|
end
|
data/lib/moonshot/controller.rb
CHANGED
@@ -1,18 +1,10 @@
|
|
1
|
-
require_relative 'ssh_target_selector'
|
2
|
-
require_relative 'ssh_command_builder'
|
3
|
-
|
4
|
-
require_relative 'stack_parameter'
|
5
|
-
require_relative 'parameter_collection'
|
6
|
-
require_relative 'parent_stack_parameter_loader'
|
7
|
-
|
8
1
|
module Moonshot
|
9
2
|
# The Controller coordinates and performs all Moonshot actions.
|
10
3
|
class Controller # rubocop:disable ClassLength
|
11
4
|
attr_accessor :config
|
12
5
|
|
13
|
-
def initialize
|
14
|
-
@config =
|
15
|
-
yield @config if block_given?
|
6
|
+
def initialize(config)
|
7
|
+
@config = config
|
16
8
|
end
|
17
9
|
|
18
10
|
def list
|
@@ -71,7 +63,7 @@ module Moonshot
|
|
71
63
|
end
|
72
64
|
end
|
73
65
|
|
74
|
-
def update # rubocop:disable AbcSize
|
66
|
+
def update(dry_run:, force:) # rubocop:disable AbcSize
|
75
67
|
# Scan the template for all required parameters and configure
|
76
68
|
# the ParameterCollection.
|
77
69
|
@config.parameters = ParameterCollection.from_template(stack.template)
|
@@ -119,7 +111,7 @@ module Moonshot
|
|
119
111
|
end
|
120
112
|
|
121
113
|
run_hook(:deploy, :pre_update)
|
122
|
-
stack.update
|
114
|
+
stack.update(dry_run: dry_run, force: force)
|
123
115
|
run_hook(:deploy, :post_update)
|
124
116
|
run_plugins(:post_update)
|
125
117
|
end
|
@@ -1,11 +1,8 @@
|
|
1
|
-
require_relative 'default_strategy'
|
2
|
-
require_relative 'ssh_config'
|
3
|
-
require_relative 'task'
|
4
|
-
require_relative 'ask_user_source'
|
5
|
-
|
6
1
|
module Moonshot
|
7
2
|
# Holds configuration for Moonshot::Controller
|
8
3
|
class ControllerConfig
|
4
|
+
attr_reader :account_alias
|
5
|
+
|
9
6
|
attr_accessor :additional_tag
|
10
7
|
attr_accessor :answer_file
|
11
8
|
attr_accessor :app_name
|
@@ -37,6 +34,8 @@ module Moonshot
|
|
37
34
|
@parameter_sources = {}
|
38
35
|
@parameters = ParameterCollection.new
|
39
36
|
@parent_stacks = []
|
37
|
+
@account_alias = nil
|
38
|
+
@per_account_config = {}
|
40
39
|
@plugins = []
|
41
40
|
@project_root = Dir.pwd
|
42
41
|
@show_all_stack_events = false
|
@@ -49,5 +48,19 @@ module Moonshot
|
|
49
48
|
user = ENV.fetch('USER', 'default-user').gsub(/\W/, '')
|
50
49
|
@environment_name = "dev-#{user}"
|
51
50
|
end
|
51
|
+
|
52
|
+
def in_account(name, &blk)
|
53
|
+
# Store account specific configs as lambdas, to be evaluated
|
54
|
+
# if the account name matches during controller execution.
|
55
|
+
@per_account_config[name] = blk
|
56
|
+
end
|
57
|
+
|
58
|
+
def update_for_account!
|
59
|
+
# Evaluated any account-specific configuration.
|
60
|
+
@account_alias = Moonshot::AccountContext.get
|
61
|
+
if @account_alias && @per_account_config.key?(account_alias)
|
62
|
+
@per_account_config[@account_alias].call(self)
|
63
|
+
end
|
64
|
+
end
|
52
65
|
end
|
53
66
|
end
|
@@ -40,6 +40,7 @@ class Moonshot::DeploymentMechanism::CodeDeploy # rubocop:disable ClassLength
|
|
40
40
|
@group_name = group_name
|
41
41
|
@codedeploy_role = role
|
42
42
|
@codedeploy_config = config_name
|
43
|
+
@ignore_app_stop_failures = false
|
43
44
|
end
|
44
45
|
|
45
46
|
def post_create_hook
|
@@ -70,13 +71,7 @@ class Moonshot::DeploymentMechanism::CodeDeploy # rubocop:disable ClassLength
|
|
70
71
|
deployment_id = nil
|
71
72
|
|
72
73
|
ilog.start_threaded 'Creating Deployment' do |s|
|
73
|
-
res =
|
74
|
-
application_name: app_name,
|
75
|
-
deployment_group_name: group_name,
|
76
|
-
revision: revision_for_artifact_repo(artifact_repo, version_name),
|
77
|
-
deployment_config_name: @codedeploy_config,
|
78
|
-
description: "Deploying version #{version_name}"
|
79
|
-
)
|
74
|
+
res = create_deployment(artifact_repo, version_name)
|
80
75
|
deployment_id = res.deployment_id
|
81
76
|
s.continue "Created Deployment #{deployment_id.blue}."
|
82
77
|
success = wait_for_deployment(deployment_id, s)
|
@@ -96,6 +91,17 @@ class Moonshot::DeploymentMechanism::CodeDeploy # rubocop:disable ClassLength
|
|
96
91
|
end
|
97
92
|
end
|
98
93
|
|
94
|
+
def deploy_cli_hook(parser)
|
95
|
+
parser.on('--ignore-app-stop-failures', TrueClass, 'Continue deployment on ApplicationStop failures') do |v| # rubocop:disable LineLength
|
96
|
+
puts "ignore = #{v}"
|
97
|
+
@ignore_app_stop_failures = v
|
98
|
+
end
|
99
|
+
|
100
|
+
parser
|
101
|
+
end
|
102
|
+
|
103
|
+
alias push_cli_hook deploy_cli_hook
|
104
|
+
|
99
105
|
private
|
100
106
|
|
101
107
|
# By default, use the stack name as the application name, unless one has been
|
@@ -333,6 +339,17 @@ class Moonshot::DeploymentMechanism::CodeDeploy # rubocop:disable ClassLength
|
|
333
339
|
}
|
334
340
|
end
|
335
341
|
|
342
|
+
def create_deployment(artifact_repo, version_name)
|
343
|
+
cd_client.create_deployment(
|
344
|
+
application_name: app_name,
|
345
|
+
deployment_group_name: group_name,
|
346
|
+
revision: revision_for_artifact_repo(artifact_repo, version_name),
|
347
|
+
deployment_config_name: @codedeploy_config,
|
348
|
+
description: "Deploying version #{version_name}",
|
349
|
+
ignore_application_stop_failures: @ignore_app_stop_failures
|
350
|
+
)
|
351
|
+
end
|
352
|
+
|
336
353
|
def doctor_check_code_deploy_role
|
337
354
|
iam_client.get_role(role_name: @codedeploy_role).role
|
338
355
|
success("#{@codedeploy_role} exists.")
|
data/lib/moonshot/stack.rb
CHANGED
@@ -1,12 +1,3 @@
|
|
1
|
-
require_relative 'creds_helper'
|
2
|
-
require_relative 'doctor_helper'
|
3
|
-
|
4
|
-
require_relative 'yaml_stack_template'
|
5
|
-
require_relative 'json_stack_template'
|
6
|
-
require_relative 'stack_parameter_printer'
|
7
|
-
require_relative 'stack_output_printer'
|
8
|
-
require_relative 'stack_asg_printer'
|
9
|
-
require_relative 'unicode_table'
|
10
1
|
require 'yaml'
|
11
2
|
|
12
3
|
module Moonshot
|
@@ -44,22 +35,21 @@ module Moonshot
|
|
44
35
|
should_wait ? wait_for_stack_state(:stack_create_complete, 'created') : true
|
45
36
|
end
|
46
37
|
|
47
|
-
def update
|
38
|
+
def update(dry_run:, force:)
|
48
39
|
raise "No stack found #{@name.blue}!" unless stack_exists?
|
49
40
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
41
|
+
change_set = ChangeSet.new(new_change_set, @name)
|
42
|
+
wait_for_change_set(change_set)
|
43
|
+
return unless change_set.valid?
|
44
|
+
|
45
|
+
if dry_run
|
46
|
+
change_set.display_changes
|
47
|
+
elsif !force
|
48
|
+
change_set.display_changes
|
49
|
+
change_set.confirm? || raise('ChangeSet rejected!')
|
58
50
|
end
|
59
51
|
|
60
|
-
|
61
|
-
raise 'Failed to update the CloudFormation Stack.' unless success
|
62
|
-
success
|
52
|
+
execute_change_set(change_set)
|
63
53
|
end
|
64
54
|
|
65
55
|
def delete
|
@@ -131,16 +121,6 @@ module Moonshot
|
|
131
121
|
end
|
132
122
|
end
|
133
123
|
|
134
|
-
# Build a hash of overrides that would be applied to this stack by an
|
135
|
-
# update.
|
136
|
-
def overrides
|
137
|
-
if File.exist?(parameters_file)
|
138
|
-
YAML.load_file(parameters_file) || {}
|
139
|
-
else
|
140
|
-
{}
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
124
|
# Return a Hash of the default values defined in the stack template.
|
145
125
|
def default_values
|
146
126
|
h = {}
|
@@ -151,25 +131,12 @@ module Moonshot
|
|
151
131
|
end
|
152
132
|
|
153
133
|
def template
|
154
|
-
|
134
|
+
load_template_file
|
155
135
|
end
|
156
136
|
|
157
137
|
# @return [String] the path to the template file.
|
158
138
|
def template_file
|
159
|
-
|
160
|
-
yaml = yaml_template_path
|
161
|
-
|
162
|
-
@template_file ||= Dir[json].first || Dir[yaml].first
|
163
|
-
|
164
|
-
raise 'CloudFormation template not found at'\
|
165
|
-
"#{json} or #{yaml}!" unless @template_file
|
166
|
-
|
167
|
-
@template_file
|
168
|
-
end
|
169
|
-
|
170
|
-
# @return [String] the path to the parameters file.
|
171
|
-
def parameters_file
|
172
|
-
File.join(@config.project_root, 'cloud_formation', 'parameters', "#{@name}.yml")
|
139
|
+
load_template_file.filename
|
173
140
|
end
|
174
141
|
|
175
142
|
private
|
@@ -178,32 +145,21 @@ module Moonshot
|
|
178
145
|
"CloudFormation Stack #{@name.blue}"
|
179
146
|
end
|
180
147
|
|
181
|
-
def json_template_path
|
182
|
-
"#{raw_template_file_name}.json"
|
183
|
-
end
|
184
|
-
|
185
|
-
def yaml_template_path
|
186
|
-
"#{raw_template_file_name}.yml"
|
187
|
-
end
|
188
|
-
|
189
|
-
# @return [String] the path to the template file without extension.
|
190
|
-
def raw_template_file_name
|
191
|
-
@raw_template_file_name ||=
|
192
|
-
File.join(@config.project_root, 'cloud_formation', @config.app_name)
|
193
|
-
end
|
194
|
-
|
195
148
|
def load_template_file
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
149
|
+
templates = [
|
150
|
+
YamlStackTemplate.new(File.join(@config.project_root, 'moonshot', 'template.yml')),
|
151
|
+
JsonStackTemplate.new(File.join(@config.project_root, 'moonshot', 'template.json')),
|
152
|
+
|
153
|
+
# Support the legacy file location from Moonshot 1.0.
|
154
|
+
YamlStackTemplate.new(
|
155
|
+
File.join(@config.project_root, 'cloud_formation', "#{@config.app_name}.yml")),
|
156
|
+
JsonStackTemplate.new(
|
157
|
+
File.join(@config.project_root, 'cloud_formation', "#{@config.app_name}.json"))
|
158
|
+
]
|
159
|
+
|
160
|
+
template = templates.find(&:exist?)
|
161
|
+
raise 'No template found in moonshot/template.{yml,json}!' unless template
|
162
|
+
template
|
207
163
|
end
|
208
164
|
|
209
165
|
def stack_parameters
|
@@ -232,20 +188,23 @@ module Moonshot
|
|
232
188
|
raise 'You are not authorized to perform create_stack calls.'
|
233
189
|
end
|
234
190
|
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
191
|
+
def new_change_set
|
192
|
+
change_set_name = [
|
193
|
+
'moonshot',
|
194
|
+
@name,
|
195
|
+
Time.now.utc.to_i.to_s
|
196
|
+
].join('-')
|
197
|
+
|
198
|
+
cf_client.create_change_set(
|
199
|
+
change_set_name: change_set_name,
|
200
|
+
description: "Moonshot update command for application '#{Moonshot.config.app_name}'",
|
239
201
|
stack_name: @name,
|
240
202
|
template_body: template.body,
|
241
203
|
capabilities: ['CAPABILITY_IAM'],
|
242
204
|
parameters: @config.parameters.values.map(&:to_cf)
|
243
205
|
)
|
244
|
-
|
245
|
-
|
246
|
-
raise e.message unless
|
247
|
-
e.message == 'No updates are to be performed.'
|
248
|
-
false
|
206
|
+
|
207
|
+
change_set_name
|
249
208
|
end
|
250
209
|
|
251
210
|
# TODO: Refactor this into it's own class.
|
@@ -335,5 +294,28 @@ module Moonshot
|
|
335
294
|
rescue => e
|
336
295
|
critical('Invalid CloudFormation template!', e.message)
|
337
296
|
end
|
297
|
+
|
298
|
+
def wait_for_change_set(change_set)
|
299
|
+
@ilog.start_threaded "Waiting for ChangeSet #{change_set.name.blue} to be created." do |s|
|
300
|
+
change_set.wait_for_change_set
|
301
|
+
|
302
|
+
if change_set.valid?
|
303
|
+
s.success "ChangeSet #{change_set.name.blue} ready!"
|
304
|
+
else
|
305
|
+
s.failure "ChangeSet failed to create: #{change_set.invalid_reason}"
|
306
|
+
end
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
def execute_change_set(change_set)
|
311
|
+
@ilog.start_threaded "Executing ChangeSet #{change_set.name.blue} for #{stack_name}." do |s|
|
312
|
+
change_set.execute
|
313
|
+
s.success "Executed ChangeSet #{change_set.name.blue} for #{stack_name}."
|
314
|
+
end
|
315
|
+
|
316
|
+
success = wait_for_stack_state(:stack_update_complete, 'updated')
|
317
|
+
raise 'Failed to update the CloudFormation Stack.' unless success
|
318
|
+
success
|
319
|
+
end
|
338
320
|
end
|
339
321
|
end
|
data/lib/plugins/backup.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: moonshot
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.1.0.beta1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Cloud Engineering <engineering@acquia.com>
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-11-
|
11
|
+
date: 2016-11-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: aws-sdk
|
@@ -184,6 +184,20 @@ dependencies:
|
|
184
184
|
- - ">="
|
185
185
|
- !ruby/object:Gem::Version
|
186
186
|
version: '0'
|
187
|
+
- !ruby/object:Gem::Dependency
|
188
|
+
name: require_all
|
189
|
+
requirement: !ruby/object:Gem::Requirement
|
190
|
+
requirements:
|
191
|
+
- - ">="
|
192
|
+
- !ruby/object:Gem::Version
|
193
|
+
version: '0'
|
194
|
+
type: :runtime
|
195
|
+
prerelease: false
|
196
|
+
version_requirements: !ruby/object:Gem::Requirement
|
197
|
+
requirements:
|
198
|
+
- - ">="
|
199
|
+
- !ruby/object:Gem::Version
|
200
|
+
version: '0'
|
187
201
|
- !ruby/object:Gem::Dependency
|
188
202
|
name: rspec
|
189
203
|
requirement: !ruby/object:Gem::Requirement
|
@@ -236,6 +250,7 @@ files:
|
|
236
250
|
- bin/moonshot
|
237
251
|
- lib/default/Moonfile.rb
|
238
252
|
- lib/moonshot.rb
|
253
|
+
- lib/moonshot/account_context.rb
|
239
254
|
- lib/moonshot/always_use_default_source.rb
|
240
255
|
- lib/moonshot/artifact_repository/s3_bucket.rb
|
241
256
|
- lib/moonshot/artifact_repository/s3_bucket_via_github_releases.rb
|
@@ -244,8 +259,10 @@ files:
|
|
244
259
|
- lib/moonshot/build_mechanism/script.rb
|
245
260
|
- lib/moonshot/build_mechanism/travis_deploy.rb
|
246
261
|
- lib/moonshot/build_mechanism/version_proxy.rb
|
262
|
+
- lib/moonshot/change_set.rb
|
247
263
|
- lib/moonshot/command.rb
|
248
264
|
- lib/moonshot/command_line.rb
|
265
|
+
- lib/moonshot/command_line_dispatcher.rb
|
249
266
|
- lib/moonshot/commands/build.rb
|
250
267
|
- lib/moonshot/commands/console.rb
|
251
268
|
- lib/moonshot/commands/create.rb
|
@@ -255,7 +272,9 @@ files:
|
|
255
272
|
- lib/moonshot/commands/list.rb
|
256
273
|
- lib/moonshot/commands/new.rb
|
257
274
|
- lib/moonshot/commands/parameter_arguments.rb
|
275
|
+
- lib/moonshot/commands/parent_stack_option.rb
|
258
276
|
- lib/moonshot/commands/push.rb
|
277
|
+
- lib/moonshot/commands/show_all_events_option.rb
|
259
278
|
- lib/moonshot/commands/ssh.rb
|
260
279
|
- lib/moonshot/commands/status.rb
|
261
280
|
- lib/moonshot/commands/update.rb
|
@@ -264,12 +283,10 @@ files:
|
|
264
283
|
- lib/moonshot/controller.rb
|
265
284
|
- lib/moonshot/controller_config.rb
|
266
285
|
- lib/moonshot/creds_helper.rb
|
267
|
-
- lib/moonshot/default_strategy.rb
|
268
286
|
- lib/moonshot/deployment_mechanism/code_deploy.rb
|
269
287
|
- lib/moonshot/doctor_helper.rb
|
270
288
|
- lib/moonshot/interactive_logger_proxy.rb
|
271
289
|
- lib/moonshot/json_stack_template.rb
|
272
|
-
- lib/moonshot/merge_strategy.rb
|
273
290
|
- lib/moonshot/parameter_collection.rb
|
274
291
|
- lib/moonshot/parent_stack_parameter_loader.rb
|
275
292
|
- lib/moonshot/resources.rb
|
@@ -315,9 +332,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
315
332
|
version: '0'
|
316
333
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
317
334
|
requirements:
|
318
|
-
- - "
|
335
|
+
- - ">"
|
319
336
|
- !ruby/object:Gem::Version
|
320
|
-
version:
|
337
|
+
version: 1.3.1
|
321
338
|
requirements: []
|
322
339
|
rubyforge_project:
|
323
340
|
rubygems_version: 2.4.8
|
@@ -1,16 +0,0 @@
|
|
1
|
-
module Moonshot
|
2
|
-
module ParameterStrategy
|
3
|
-
# Default strategy: use parameter defined in the parameter file
|
4
|
-
class DefaultStrategy
|
5
|
-
def parameters(params, _, _)
|
6
|
-
params.map do |key, _|
|
7
|
-
{
|
8
|
-
parameter_key: key,
|
9
|
-
parameter_value: params[key],
|
10
|
-
use_previous_value: false
|
11
|
-
}
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
@@ -1,31 +0,0 @@
|
|
1
|
-
require 'highline/import'
|
2
|
-
require_relative 'unicode_table'
|
3
|
-
|
4
|
-
module Moonshot
|
5
|
-
module ParameterStrategy
|
6
|
-
# Merge strategy: prefer parameter values defined in the parameter file,
|
7
|
-
# otherwise use the previously set value on the existing stack.
|
8
|
-
class MergeStrategy
|
9
|
-
def parameters(params, stack_params, template)
|
10
|
-
stack_keys = stack_params.keys.select do |k|
|
11
|
-
template.parameters.any? { |p| p.name == k }
|
12
|
-
end
|
13
|
-
|
14
|
-
(params.keys + stack_keys).uniq.map do |key|
|
15
|
-
if params[key]
|
16
|
-
{
|
17
|
-
parameter_key: key,
|
18
|
-
parameter_value: params[key],
|
19
|
-
use_previous_value: false
|
20
|
-
}
|
21
|
-
else
|
22
|
-
{
|
23
|
-
parameter_key: key,
|
24
|
-
use_previous_value: true
|
25
|
-
}
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|