moonshot 1.1.0.beta4 → 2.0.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/command_line_dispatcher.rb +2 -1
- data/lib/moonshot/deployment_mechanism/code_deploy.rb +7 -3
- data/lib/plugins/backup.rb +89 -16
- data/lib/plugins/encrypted_parameters.rb +72 -66
- data/lib/plugins/encrypted_parameters/kms_key.rb +17 -15
- data/lib/plugins/encrypted_parameters/parameter_encrypter.rb +18 -16
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5d2a30d2ed3a9a3f146a0bcf4972f0bfc9baff41
|
4
|
+
data.tar.gz: 0087b7d90297d013cffd9b787f88b720dc357212
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6c1e1938bd414ec74f03185c824cf45e4c3f101b7b4acbaabbec46d71f17bf018b56600201eb1445c6b8b9c14c9c81223819c428a4667f4f67e69a90ddde5ba2
|
7
|
+
data.tar.gz: c7262c61bd51cf6febe8f31cee9a8ef64831580aeaacba2b883725b5f36d38389db5337d456ba693c03249c1f24d68df3c29ab9d47cb088930793dd23aa9cfed
|
@@ -16,7 +16,8 @@ module Moonshot
|
|
16
16
|
parser = build_parser(handler)
|
17
17
|
parser.parse!
|
18
18
|
|
19
|
-
|
19
|
+
req_arguments = handler.method(:execute).parameters.select { |arg| arg[0] == :req }
|
20
|
+
if ARGV.size < req_arguments.size
|
20
21
|
warn handler.parser.help
|
21
22
|
raise "Invalid command line for '#{@command}'."
|
22
23
|
end
|
@@ -307,9 +307,13 @@ class Moonshot::DeploymentMechanism::CodeDeploy # rubocop:disable ClassLength
|
|
307
307
|
inst_summary.lifecycle_events.each do |event|
|
308
308
|
next unless event.status == 'Failed'
|
309
309
|
|
310
|
-
|
311
|
-
|
312
|
-
|
310
|
+
if event.diagnostics.nil?
|
311
|
+
ilog.error('Lifecycle event chain is not available.')
|
312
|
+
else
|
313
|
+
ilog.error(event.diagnostics.message)
|
314
|
+
event.diagnostics.log_tail.each_line do |line|
|
315
|
+
ilog.error(line)
|
316
|
+
end
|
313
317
|
end
|
314
318
|
end
|
315
319
|
end
|
data/lib/plugins/backup.rb
CHANGED
@@ -1,23 +1,24 @@
|
|
1
1
|
require 'rubygems/package'
|
2
2
|
require 'zlib'
|
3
|
+
require 'yaml'
|
3
4
|
|
4
5
|
module Moonshot
|
5
6
|
module Plugins
|
6
7
|
# Moonshot plugin class for deflating and uploading files on given hooks
|
7
|
-
class Backup
|
8
|
+
class Backup # rubocop:disable Metrics/ClassLength
|
8
9
|
include Moonshot::CredsHelper
|
9
10
|
|
10
11
|
attr_accessor :bucket,
|
11
12
|
:buckets,
|
12
13
|
:files,
|
13
14
|
:hooks,
|
14
|
-
:target_name
|
15
|
+
:target_name,
|
16
|
+
:backup_parameters,
|
17
|
+
:backup_template
|
15
18
|
|
16
19
|
def initialize
|
17
20
|
yield self if block_given?
|
18
|
-
|
19
|
-
if @files.nil? || @files.empty? || @hooks.nil? || !(@bucket.nil? ^ @buckets.nil?)
|
20
|
-
|
21
|
+
validate_configuration
|
21
22
|
@target_name ||= '%{app_name}_%{timestamp}_%{user}.tar.gz'
|
22
23
|
end
|
23
24
|
|
@@ -29,10 +30,8 @@ module Moonshot
|
|
29
30
|
raise ArgumentError if bucket.nil? || bucket.empty?
|
30
31
|
Moonshot::Plugins::Backup.new do |b|
|
31
32
|
b.bucket = bucket
|
32
|
-
b.
|
33
|
-
|
34
|
-
'cloud_formation/parameters/%{stack_name}.yml'
|
35
|
-
]
|
33
|
+
b.backup_parameters = true
|
34
|
+
b.backup_template = true
|
36
35
|
b.hooks = [:post_create, :post_update]
|
37
36
|
end
|
38
37
|
end
|
@@ -44,10 +43,11 @@ module Moonshot
|
|
44
43
|
def backup(resources)
|
45
44
|
raise ArgumentError if resources.nil?
|
46
45
|
|
47
|
-
@app_name = resources.
|
46
|
+
@app_name = resources.controller.config.app_name
|
48
47
|
@stack_name = resources.stack.name
|
49
48
|
@target_name = render(@target_name)
|
50
49
|
@target_bucket = define_bucket
|
50
|
+
@parameters = resources.stack.parameters
|
51
51
|
|
52
52
|
return if @target_bucket.nil?
|
53
53
|
|
@@ -58,7 +58,7 @@ module Moonshot
|
|
58
58
|
upload(zip_out)
|
59
59
|
|
60
60
|
s.success("#{log_message} succeeded.")
|
61
|
-
rescue
|
61
|
+
rescue => e
|
62
62
|
s.failure("#{log_message} failed: #{e}")
|
63
63
|
ensure
|
64
64
|
tar_out.close unless tar_out.nil?
|
@@ -90,18 +90,59 @@ module Moonshot
|
|
90
90
|
def tar(target_files)
|
91
91
|
tar_stream = StringIO.new
|
92
92
|
Gem::Package::TarWriter.new(tar_stream) do |writer|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
93
|
+
# adding user files
|
94
|
+
unless target_files.nil? || target_files.empty?
|
95
|
+
target_files.each do |file|
|
96
|
+
file = render(file)
|
97
|
+
add_file_to_tar(writer, file)
|
98
98
|
end
|
99
99
|
end
|
100
|
+
|
101
|
+
# adding parameters
|
102
|
+
if @backup_parameters
|
103
|
+
add_str_to_tar(
|
104
|
+
writer,
|
105
|
+
render('%{stack_name}-parameters.yml'),
|
106
|
+
@parameters
|
107
|
+
)
|
108
|
+
end
|
109
|
+
|
110
|
+
# adding template file
|
111
|
+
if @backup_template
|
112
|
+
template_file_path = render('cloud_formation/%{app_name}.json')
|
113
|
+
add_file_to_tar(writer, template_file_path)
|
114
|
+
end
|
100
115
|
end
|
101
116
|
tar_stream.seek(0)
|
102
117
|
tar_stream
|
103
118
|
end
|
104
119
|
|
120
|
+
# Helper method to add a file to an inmemory tar archive.
|
121
|
+
#
|
122
|
+
# @param writer [TarWriter]
|
123
|
+
# @param file_name [String]
|
124
|
+
def add_file_to_tar(writer, file_name)
|
125
|
+
writer.add_file(File.basename(file_name), 0644) do |io|
|
126
|
+
begin
|
127
|
+
File.open(file_name, 'r') { |f| io.write(f.read) }
|
128
|
+
rescue Errno::ENOENT
|
129
|
+
warn "'#{file_name}' was not found."
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# Helper method to add a file based on an input String as content
|
135
|
+
# to an inmemory tar archive.
|
136
|
+
#
|
137
|
+
# @param writer [TarWriter]
|
138
|
+
# @param target_filename [String]
|
139
|
+
# @param content [String]
|
140
|
+
def add_str_to_tar(writer, target_filename, content)
|
141
|
+
writer.add_file(File.basename(target_filename), 0644) do |io|
|
142
|
+
io.write(content.to_yaml)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
105
146
|
# Create a zip archive in memory, returning the IO object pointing at the
|
106
147
|
# beginning of the zipfile.
|
107
148
|
#
|
@@ -168,6 +209,38 @@ module Moonshot
|
|
168
209
|
def bucket_by_account(account)
|
169
210
|
@buckets[account]
|
170
211
|
end
|
212
|
+
|
213
|
+
def validate_configuration
|
214
|
+
validate_buckets
|
215
|
+
validate_redundant_configuration
|
216
|
+
validate_targets
|
217
|
+
validate_hooks
|
218
|
+
end
|
219
|
+
|
220
|
+
def validate_buckets
|
221
|
+
raise ArgumentError, 'You must specify a target bucket.' \
|
222
|
+
if (@bucket.nil? || @bucket.empty?) \
|
223
|
+
&& (@buckets.nil? || @buckets.empty?)
|
224
|
+
end
|
225
|
+
|
226
|
+
def validate_redundant_configuration
|
227
|
+
raise ArgumentError, 'You can not specify both `bucket` and `buckets`.' \
|
228
|
+
if @bucket && @buckets
|
229
|
+
end
|
230
|
+
|
231
|
+
def validate_targets
|
232
|
+
raise ArgumentError, 'You must specify files to back up.' \
|
233
|
+
if (@files.nil? || @files.empty?) \
|
234
|
+
&& (!@backup_parameters && !@backup_template)
|
235
|
+
end
|
236
|
+
|
237
|
+
def validate_hooks
|
238
|
+
raise ArgumentError, 'You must specify a hook / hooks to run the backup on.' \
|
239
|
+
if hooks.nil? || hooks.empty?
|
240
|
+
|
241
|
+
raise ArgumentError, '`pre_create` and `post_delete` hooks are not supported.' \
|
242
|
+
if hooks.include?(:pre_create) || hooks.include?(:post_delete)
|
243
|
+
end
|
171
244
|
end
|
172
245
|
end
|
173
246
|
end
|
@@ -20,92 +20,98 @@
|
|
20
20
|
# c.parameter_sources['KMSKey1'] = Moonshot::AlwaysUseDefaultSource.new
|
21
21
|
# end
|
22
22
|
module Moonshot
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
23
|
+
module Plugins
|
24
|
+
class EncryptedParameters
|
25
|
+
# @param [String] kms_key_parameter_name
|
26
|
+
# The parameter name to store the KMS Key ARN as.
|
27
|
+
# @param [Array<String>] parameters
|
28
|
+
# Names of parameters to encrypt, if they are not already set.
|
29
|
+
def initialize(kms_key_parameter_name, parameters)
|
30
|
+
@kms_key_parameter_name = kms_key_parameter_name
|
31
|
+
@parameters = parameters
|
32
|
+
@delete_key = true
|
33
|
+
end
|
33
34
|
|
34
|
-
|
35
|
-
|
35
|
+
def pre_create(res)
|
36
|
+
@ilog = res.ilog
|
36
37
|
|
37
|
-
|
38
|
-
|
38
|
+
key_arn = find_or_create_kms_key
|
39
|
+
pe = ParameterEncrypter.new(key_arn)
|
39
40
|
|
40
|
-
|
41
|
-
|
42
|
-
|
41
|
+
@parameters.each do |parameter_name|
|
42
|
+
sp = Moonshot.config.parameters[parameter_name]
|
43
|
+
raise "No such parameter #{parameter_name}" unless sp
|
43
44
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
45
|
+
@ilog.start_threaded "Handling encrypted parameter #{parameter_name.blue}..." do |s|
|
46
|
+
if sp.use_previous?
|
47
|
+
# TODO: Remove this and the one below when the upstream race is fixed.
|
48
|
+
# See https://github.com/askreet/interactive-logger/issues/7
|
49
|
+
sleep 0.05
|
50
|
+
s.success "Using previous encrypted value for #{parameter_name.blue}."
|
51
|
+
elsif !sp.set? && !sp.default?
|
52
|
+
# If the parameter isn't set, we can't encrypt it. Doing
|
53
|
+
# nothing means we will give the user a friendly error message
|
54
|
+
# about unset parameters when the controller resumes.
|
55
|
+
sleep 0.05
|
56
|
+
s.failure "No value to encrypt for #{parameter_name.blue}!"
|
57
|
+
else
|
58
|
+
s.continue "Encrypting new value for parameter #{parameter_name.blue}..."
|
59
|
+
Moonshot.config.parameters[sp.name].set(pe.encrypt(sp.value))
|
60
|
+
s.success "Encrypted new value for parameter #{parameter_name.blue}!"
|
61
|
+
end
|
60
62
|
end
|
61
63
|
end
|
62
64
|
end
|
63
|
-
|
64
|
-
alias pre_update pre_create
|
65
|
+
alias pre_update pre_create
|
65
66
|
|
66
|
-
|
67
|
-
|
67
|
+
def post_delete(res)
|
68
|
+
key_arn = Moonshot.config.parameters[@kms_key_parameter_name].value
|
68
69
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
70
|
+
res.ilog.start_threaded "Cleaning up KMS Key #{@kms_key_parameter_name.blue}..." do |s|
|
71
|
+
if @delete_key
|
72
|
+
KmsKey.new(key_arn).delete
|
73
|
+
s.success "Deleted KMS Key #{@kms_key_parameter_name.blue}!"
|
74
|
+
else
|
75
|
+
# TODO: See above.
|
76
|
+
sleep 0.05
|
77
|
+
s.success "Retained KMS Key #{@kms_key_parameter_name.blue}."
|
78
|
+
end
|
77
79
|
end
|
78
80
|
end
|
79
|
-
end
|
80
81
|
|
81
|
-
|
82
|
-
|
83
|
-
|
82
|
+
def delete_cli_hook(parser)
|
83
|
+
parser.on(
|
84
|
+
'--retain-kms-key',
|
85
|
+
TrueClass,
|
86
|
+
'Do not delete the KMS Key for this environment.'
|
87
|
+
) do
|
88
|
+
@delete_key = false
|
89
|
+
end
|
84
90
|
end
|
85
|
-
end
|
86
91
|
|
87
|
-
|
92
|
+
private
|
88
93
|
|
89
|
-
|
90
|
-
|
94
|
+
def find_or_create_kms_key
|
95
|
+
key_arn = nil
|
91
96
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
97
|
+
@ilog.start_threaded "Checking for KMS Key #{@kms_key_parameter_name}" do |s|
|
98
|
+
if Moonshot.config.parameters.key?(@kms_key_parameter_name)
|
99
|
+
if 'Auto' == Moonshot.config.parameters[@kms_key_parameter_name].value
|
100
|
+
s.continue "Auto-generating KMS Key for #{@kms_key_parameter_name.blue}... "
|
101
|
+
key_arn = KmsKey.create.arn
|
102
|
+
Moonshot.config.parameters[@kms_key_parameter_name].set(key_arn)
|
103
|
+
s.success "Created a new KMS Key for #{@kms_key_parameter_name.blue}!"
|
104
|
+
else
|
105
|
+
key_arn = KmsKey.new(Moonshot.config.parameters[@kms_key_parameter_name].value).arn
|
106
|
+
s.success "Using existing KMS Key for #{@kms_key_parameter_name.blue}!"
|
107
|
+
end
|
102
108
|
end
|
103
109
|
end
|
104
|
-
end
|
105
110
|
|
106
|
-
|
111
|
+
raise "No such Stack Parameter #{@kms_key_parameter_name}!" unless key_arn
|
107
112
|
|
108
|
-
|
113
|
+
key_arn
|
114
|
+
end
|
109
115
|
end
|
110
116
|
end
|
111
117
|
end
|
@@ -1,23 +1,25 @@
|
|
1
1
|
module Moonshot
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
2
|
+
module Plugins
|
3
|
+
class EncryptedParameters
|
4
|
+
# Class that manages KMS keys in AWS.
|
5
|
+
class KmsKey
|
6
|
+
attr_reader :arn
|
6
7
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
8
|
+
def initialize(arn)
|
9
|
+
@arn = arn
|
10
|
+
@kms_client = Aws::KMS::Client.new
|
11
|
+
end
|
11
12
|
|
12
|
-
|
13
|
-
|
14
|
-
|
13
|
+
def self.create
|
14
|
+
resp = Aws::KMS::Client.new.create_key
|
15
|
+
arn = resp.key_metadata.arn
|
15
16
|
|
16
|
-
|
17
|
-
|
17
|
+
new(arn)
|
18
|
+
end
|
18
19
|
|
19
|
-
|
20
|
-
|
20
|
+
def delete
|
21
|
+
@kms_client.schedule_key_deletion(key_id: @arn, pending_window_in_days: 7)
|
22
|
+
end
|
21
23
|
end
|
22
24
|
end
|
23
25
|
end
|
@@ -1,23 +1,25 @@
|
|
1
1
|
require 'base64'
|
2
2
|
module Moonshot
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
3
|
+
module Plugins
|
4
|
+
class EncryptedParameters
|
5
|
+
# Class that can encrypt and decrypt parameters using KMS.
|
6
|
+
class ParameterEncrypter
|
7
|
+
# @param [String] key_arn The ARN for the KMS key.
|
8
|
+
def initialize(key_arn)
|
9
|
+
@kms_client = Aws::KMS::Client.new
|
10
|
+
@key_arn = key_arn
|
11
|
+
end
|
11
12
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
13
|
+
# Encrypt and base64 encode the parameter value.
|
14
|
+
#
|
15
|
+
# @param [String] param_value The parameter to encrypt.
|
16
|
+
# @return [String] base64 encoded encrypted ciphertext.
|
17
|
+
def encrypt(param_value)
|
18
|
+
resp = @kms_client.encrypt(key_id: @key_arn, plaintext: param_value)
|
18
19
|
|
19
|
-
|
20
|
-
|
20
|
+
# Use strict here to avoid newlines which cause issues with parameters.
|
21
|
+
Base64.strict_encode64(resp.ciphertext_blob)
|
22
|
+
end
|
21
23
|
end
|
22
24
|
end
|
23
25
|
end
|
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:
|
4
|
+
version: 2.0.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: 2017-
|
11
|
+
date: 2017-02-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: aws-sdk
|