moonshot 1.0.0 → 1.1.0.beta1
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/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
|