sumomo 0.8.22 → 0.10.0
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/README.md +13 -4
- data/data/sumomo/custom_resources/TempS3Bucket.js +34 -1
- data/exe/sumomo +7 -14
- data/lib/sumomo/ec2.rb +6 -6
- data/lib/sumomo/version.rb +1 -1
- data/lib/sumomo.rb +191 -13
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 346a647def575daedb5dc3e3eba04a73024c441be3ae92740cf9edb668992e44
|
4
|
+
data.tar.gz: 379cbf589f7ace6b8da717054ab71754604ed6b863808aadf3b6996a18df292f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a0cd59b9b12bf2ec4e1b968f37962c505e9c9e6b7e47ed8846ad7af3737c162f5947bd4e01bfa1cdddfd4d347c378803db10c25ceadf56c5d7d31e0cecbe6b26
|
7
|
+
data.tar.gz: ed8757d5ca20a22791c1ded35a453ffd00f51260e6f5a96fb38adc9cc55df4617d30dbfa4a83bad353d2758bdc1232f51183e1bdbf09591c6142f6e92d98ff84
|
data/README.md
CHANGED
@@ -40,19 +40,28 @@ output "Haha", x
|
|
40
40
|
|
41
41
|
To create a stack that acquires an IP Address and outputs it
|
42
42
|
|
43
|
-
|
43
|
+
$ sumomo create mystack
|
44
44
|
|
45
45
|
To view a stack's outputs
|
46
46
|
|
47
|
-
|
47
|
+
$ sumomo outputs mystack
|
48
48
|
|
49
49
|
To update a stack
|
50
50
|
|
51
|
-
|
51
|
+
$ sumomo update mystack
|
52
52
|
|
53
53
|
To delete a stack
|
54
54
|
|
55
|
-
|
55
|
+
$ sumomo delete mystack
|
56
|
+
|
57
|
+
To view what changes your stuff will cause to your stack
|
58
|
+
|
59
|
+
$ sumomo diff mystack
|
60
|
+
|
61
|
+
To see the raw cloudformation template
|
62
|
+
|
63
|
+
$ sumomo show mystack
|
64
|
+
|
56
65
|
|
57
66
|
## Library Usage
|
58
67
|
|
@@ -2,6 +2,7 @@ var s3 = new aws.S3();
|
|
2
2
|
var name = request.ResourceProperties.BucketName;
|
3
3
|
var copy_from_dir = request.ResourceProperties.CopyFromDirectory;
|
4
4
|
var copy_from_bucket = request.ResourceProperties.CopyFromBucket;
|
5
|
+
var disable_acl = request.ResourceProperties.DisableACL;
|
5
6
|
var success_obj = {
|
6
7
|
Arn: "arn:aws:s3:::" + name,
|
7
8
|
DomainName: name + ".s3-" + request.ResourceProperties.Region + ".amazonaws.com"
|
@@ -72,6 +73,38 @@ function copy_files(success, fail)
|
|
72
73
|
);
|
73
74
|
}
|
74
75
|
|
76
|
+
function apply_ownership_policy(name, success, fail)
|
77
|
+
{
|
78
|
+
if (disable_acl)
|
79
|
+
{
|
80
|
+
// ACL is disabled by default, so we simply skip this step.
|
81
|
+
return copy_files(success, fail);
|
82
|
+
}
|
83
|
+
|
84
|
+
var params = {
|
85
|
+
Bucket: name,
|
86
|
+
OwnershipControls: {
|
87
|
+
Rules: [
|
88
|
+
{
|
89
|
+
ObjectOwnership: "BucketOwnerPreferred"
|
90
|
+
}
|
91
|
+
]
|
92
|
+
}
|
93
|
+
};
|
94
|
+
|
95
|
+
s3.putBucketOwnershipControls(params, function(err, data)
|
96
|
+
{
|
97
|
+
if (err)
|
98
|
+
{
|
99
|
+
fail(err);
|
100
|
+
}
|
101
|
+
else
|
102
|
+
{
|
103
|
+
copy_files(success, fail);
|
104
|
+
}
|
105
|
+
});
|
106
|
+
}
|
107
|
+
|
75
108
|
function create_bucket(name, success, fail)
|
76
109
|
{
|
77
110
|
var create_params = {
|
@@ -89,7 +122,7 @@ function create_bucket(name, success, fail)
|
|
89
122
|
}
|
90
123
|
else
|
91
124
|
{
|
92
|
-
|
125
|
+
apply_ownership_policy(name, success, fail);
|
93
126
|
}
|
94
127
|
});
|
95
128
|
}
|
data/exe/sumomo
CHANGED
@@ -5,12 +5,12 @@ require 'trollop'
|
|
5
5
|
require 'sumomo'
|
6
6
|
require 'yaml'
|
7
7
|
|
8
|
-
SUB_COMMANDS = %w[delete create update outputs testapi].freeze
|
8
|
+
SUB_COMMANDS = %w[delete create update outputs show diff testapi].freeze
|
9
9
|
global_opts = Trollop.options do
|
10
10
|
banner <<-USAGE
|
11
11
|
Sumomo v#{Sumomo::VERSION}
|
12
12
|
|
13
|
-
Usage: sumomo [options]
|
13
|
+
Usage: sumomo [options] <#{SUB_COMMANDS.join('|')}> <stackname>
|
14
14
|
USAGE
|
15
15
|
|
16
16
|
opt :region, 'AWS region to use', type: :string, default: 'ap-northeast-1'
|
@@ -30,21 +30,12 @@ cmd_opts = case cmd
|
|
30
30
|
when 'delete'
|
31
31
|
Sumomo.delete_stack(name: ARGV[0], region: global_opts[:region])
|
32
32
|
|
33
|
-
when 'create'
|
34
|
-
local_opts = Trollop.options do
|
35
|
-
opt :filename, 'File that describes the stack', type: :string, default: 'Sumomofile'
|
36
|
-
end
|
37
|
-
Sumomo.create_stack(name: ARGV[0], region: global_opts[:region]) do
|
38
|
-
proc = proc {}
|
39
|
-
eval File.read(local_opts[:filename]), proc.binding, local_opts[:filename]
|
40
|
-
end
|
41
|
-
|
42
|
-
when 'update'
|
33
|
+
when 'create', 'update', 'show', 'diff'
|
43
34
|
local_opts = Trollop.options do
|
44
35
|
opt :filename, 'File that describes the stack', type: :string, default: 'Sumomofile'
|
45
36
|
opt :changeset, 'Create a changeset instead of directly update', type: :boolean, default: false
|
46
37
|
end
|
47
|
-
Sumomo.
|
38
|
+
Sumomo.manage_stack(name: ARGV[0], cmd: cmd, changeset: !!local_opts[:changeset], region: global_opts[:region]) do
|
48
39
|
proc = proc {}
|
49
40
|
eval File.read(local_opts[:filename]), proc.binding, local_opts[:filename]
|
50
41
|
end
|
@@ -80,4 +71,6 @@ cmd_opts = case cmd
|
|
80
71
|
Trollop.die "Unknown subcommand #{cmd.inspect}"
|
81
72
|
end
|
82
73
|
|
83
|
-
|
74
|
+
unless %w[show diff].include?(cmd)
|
75
|
+
Sumomo.wait_for_stack(name: ARGV[0], region: global_opts[:region])
|
76
|
+
end
|
data/lib/sumomo/ec2.rb
CHANGED
@@ -277,7 +277,7 @@ module Sumomo
|
|
277
277
|
@ami_lookup_resources ||= {}
|
278
278
|
|
279
279
|
unless @ami_lookup_resources[type]
|
280
|
-
@ami_lookup_resources[type] = make 'Custom::AMILookup' do
|
280
|
+
@ami_lookup_resources[type] = make 'Custom::AMILookup', name: "#{name}AmiLookup" do
|
281
281
|
InstanceType type
|
282
282
|
end
|
283
283
|
end
|
@@ -335,14 +335,14 @@ module Sumomo
|
|
335
335
|
end
|
336
336
|
raise 'ec2: egress option needs to be an array' unless egress.is_a? Array
|
337
337
|
|
338
|
-
web_sec_group = make 'AWS::EC2::SecurityGroup' do
|
338
|
+
web_sec_group = make 'AWS::EC2::SecurityGroup', name: "#{name}SecurityGroup" do
|
339
339
|
GroupDescription "Security group for layer: #{layer}"
|
340
340
|
SecurityGroupIngress ingress
|
341
341
|
SecurityGroupEgress egress
|
342
342
|
VpcId network.vpc
|
343
343
|
end
|
344
344
|
|
345
|
-
wait_handle = make 'AWS::CloudFormation::WaitConditionHandle'
|
345
|
+
wait_handle = make 'AWS::CloudFormation::WaitConditionHandle', name: "#{name}WaitConditionHandle"
|
346
346
|
|
347
347
|
user_data = initscript(wait_handle, name, call('Fn::Join', "\n", script_arr))
|
348
348
|
|
@@ -355,7 +355,7 @@ module Sumomo
|
|
355
355
|
}]
|
356
356
|
}
|
357
357
|
|
358
|
-
asg_role = make 'AWS::IAM::Role' do
|
358
|
+
asg_role = make 'AWS::IAM::Role', name: "#{name}Role" do
|
359
359
|
AssumeRolePolicyDocument role_policy_doc
|
360
360
|
Path '/'
|
361
361
|
Policies [{
|
@@ -404,12 +404,12 @@ module Sumomo
|
|
404
404
|
}]
|
405
405
|
end
|
406
406
|
|
407
|
-
asg_profile = make 'AWS::IAM::InstanceProfile' do
|
407
|
+
asg_profile = make 'AWS::IAM::InstanceProfile', name: "#{name}InstanceProfile" do
|
408
408
|
Path '/'
|
409
409
|
Roles [asg_role]
|
410
410
|
end
|
411
411
|
|
412
|
-
launch_config = make 'AWS::AutoScaling::LaunchConfiguration' do
|
412
|
+
launch_config = make 'AWS::AutoScaling::LaunchConfiguration', name: "#{name}LaunchConfiguration" do
|
413
413
|
AssociatePublicIpAddress has_public_ips
|
414
414
|
KeyName keypair
|
415
415
|
SecurityGroups [web_sec_group] + security_groups
|
data/lib/sumomo/version.rb
CHANGED
data/lib/sumomo.rb
CHANGED
@@ -26,17 +26,7 @@ module Sumomo
|
|
26
26
|
"cloudformation/#{make_master_key_name(name: name)}.pem"
|
27
27
|
end
|
28
28
|
|
29
|
-
def self.
|
30
|
-
cf = Aws::CloudFormation::Client.new(region: region)
|
31
|
-
begin
|
32
|
-
cf.describe_stacks(stack_name: name)
|
33
|
-
raise "There is already a stack named '#{name}'"
|
34
|
-
rescue Aws::CloudFormation::Errors::ValidationError
|
35
|
-
update_stack(name: name, region: region, sns_arn: sns_arn, &block)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
def self.update_stack(name:, region:, sns_arn: nil, changeset: false, &block)
|
29
|
+
def self.manage_stack(name:, region:, cmd:'update', sns_arn: nil, changeset: false, &block)
|
40
30
|
cf = Aws::CloudFormation::Client.new(region: region)
|
41
31
|
s3 = Aws::S3::Client.new(region: region)
|
42
32
|
ec2 = Aws::EC2::Client.new(region: region)
|
@@ -107,9 +97,66 @@ module Sumomo
|
|
107
97
|
hidden_values = @hidden_values
|
108
98
|
end.templatize
|
109
99
|
|
110
|
-
|
100
|
+
templatedata = JSON.parse(template)
|
101
|
+
|
102
|
+
if cmd == 'show'
|
103
|
+
puts templatedata.to_yaml
|
104
|
+
return
|
105
|
+
end
|
106
|
+
|
107
|
+
if cmd == 'diff'
|
108
|
+
store.set_raw('cloudformation/temporary_template', template)
|
109
|
+
update_options = {
|
110
|
+
stack_name: name,
|
111
|
+
template_url: store.url('cloudformation/temporary_template'),
|
112
|
+
parameters: hidden_values,
|
113
|
+
capabilities: ['CAPABILITY_IAM', 'CAPABILITY_NAMED_IAM']
|
114
|
+
}
|
115
|
+
|
116
|
+
change_set_name = "CanaryChange#{curtimestr}"
|
117
|
+
|
118
|
+
puts "Create change set"
|
119
|
+
|
120
|
+
cf.create_change_set(
|
121
|
+
**update_options,
|
122
|
+
change_set_name: change_set_name
|
123
|
+
)
|
124
|
+
|
125
|
+
resp = nil
|
126
|
+
|
127
|
+
puts "Wait for change set"
|
128
|
+
|
129
|
+
loop do
|
130
|
+
sleep 5
|
131
|
+
resp = cf.describe_change_set({
|
132
|
+
change_set_name: change_set_name,
|
133
|
+
stack_name: name
|
134
|
+
})
|
135
|
+
puts "#{change_set_name} #{resp.status}"
|
136
|
+
|
137
|
+
break unless resp.status.end_with? 'IN_PROGRESS'
|
138
|
+
end
|
139
|
+
|
140
|
+
puts "Cleaning up..."
|
141
|
+
|
142
|
+
cf.delete_change_set({
|
143
|
+
change_set_name: change_set_name,
|
144
|
+
stack_name: name,
|
145
|
+
})
|
146
|
+
|
147
|
+
puts 'Change list'
|
148
|
+
|
149
|
+
clist = process_changeset_response(resp)
|
150
|
+
drifts = get_drifts(templatedata, cf, name)
|
111
151
|
|
112
|
-
|
152
|
+
# puts clist.to_yaml
|
153
|
+
# puts drifts.to_yaml
|
154
|
+
puts humanize_changeset_response(clist, drifts, templatedata).to_yaml
|
155
|
+
|
156
|
+
return
|
157
|
+
end
|
158
|
+
|
159
|
+
# TODO: if the template is too big, split it into nested templates
|
113
160
|
|
114
161
|
store.set_raw('cloudformation/template', template)
|
115
162
|
|
@@ -144,6 +191,137 @@ module Sumomo
|
|
144
191
|
end
|
145
192
|
end
|
146
193
|
|
194
|
+
def self.get_drifts(templatedata, cf, stack_name)
|
195
|
+
drifts = {}
|
196
|
+
|
197
|
+
templatedata['Resources'].each do |k,info|
|
198
|
+
puts "detect drift for #{k}"
|
199
|
+
|
200
|
+
rd = cf.detect_stack_resource_drift({
|
201
|
+
stack_name: stack_name, # required
|
202
|
+
logical_resource_id: k, # required
|
203
|
+
})
|
204
|
+
|
205
|
+
diffinfo = {}
|
206
|
+
differences = []
|
207
|
+
|
208
|
+
rd.stack_resource_drift.property_differences.each do |pd|
|
209
|
+
res = {}
|
210
|
+
|
211
|
+
%w[property_path expected_value actual_value difference_type].each do |att|
|
212
|
+
res[att] = pd.send(att.to_sym)
|
213
|
+
end
|
214
|
+
|
215
|
+
differences << res
|
216
|
+
end
|
217
|
+
|
218
|
+
%w[resource_type expected_properties actual_properties stack_resource_drift_status].each do |key|
|
219
|
+
diffinfo[key] = rd.stack_resource_drift.send(key.to_sym)
|
220
|
+
end
|
221
|
+
|
222
|
+
diffinfo['differences'] = differences
|
223
|
+
|
224
|
+
drifts[k] = diffinfo
|
225
|
+
|
226
|
+
rescue Aws::CloudFormation::Errors::ValidationError => e
|
227
|
+
drifts[k] = {
|
228
|
+
"exception" => e.message
|
229
|
+
}
|
230
|
+
|
231
|
+
end
|
232
|
+
|
233
|
+
drifts
|
234
|
+
end
|
235
|
+
|
236
|
+
def self.process_changeset_response(resp)
|
237
|
+
result = {}
|
238
|
+
|
239
|
+
resp.changes.each do |change|
|
240
|
+
info = {}
|
241
|
+
|
242
|
+
%w[action physical_resource_id resource_type replacement].each do |k|
|
243
|
+
info[k] = change.resource_change.send(k.to_sym)
|
244
|
+
end
|
245
|
+
|
246
|
+
details = []
|
247
|
+
|
248
|
+
change.resource_change.details.each do |detail|
|
249
|
+
|
250
|
+
detailinfo = {}
|
251
|
+
|
252
|
+
%w[attribute name requires_recreation].each do |k|
|
253
|
+
detailinfo[k] = detail.target.send(k.to_sym)
|
254
|
+
end
|
255
|
+
|
256
|
+
%w[evaluation change_source causing_entity].each do |k|
|
257
|
+
detailinfo[k] = detail.send(k.to_sym)
|
258
|
+
end
|
259
|
+
|
260
|
+
details << detailinfo
|
261
|
+
end
|
262
|
+
|
263
|
+
info['details'] = details
|
264
|
+
|
265
|
+
result[change.resource_change.logical_resource_id] = info
|
266
|
+
end
|
267
|
+
|
268
|
+
result
|
269
|
+
end
|
270
|
+
|
271
|
+
def self.humanize_changeset_response(clist, drifts, templatedata)
|
272
|
+
# refs
|
273
|
+
# https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/CloudFormation/Types/ResourceChange.html
|
274
|
+
# https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/CloudFormation/Types/ResourceTargetDefinition.html
|
275
|
+
|
276
|
+
result = {}
|
277
|
+
|
278
|
+
clist.each do |k,info|
|
279
|
+
|
280
|
+
if info['action'] == 'Modify'
|
281
|
+
|
282
|
+
z = if info['replacement'] != 'False'
|
283
|
+
result['Replace'] ||= {}
|
284
|
+
else
|
285
|
+
result['Modify'] ||= {}
|
286
|
+
end
|
287
|
+
|
288
|
+
z[k] = {}
|
289
|
+
|
290
|
+
info['details'].each do |detail|
|
291
|
+
|
292
|
+
propname = detail['attribute']
|
293
|
+
if detail['attribute'] == 'Properties'
|
294
|
+
propname = detail['name']
|
295
|
+
end
|
296
|
+
|
297
|
+
fr = templatedata['Resources'][k]
|
298
|
+
tr = drifts[k]
|
299
|
+
|
300
|
+
fromprops = fr['Properties']
|
301
|
+
toprops = {}
|
302
|
+
|
303
|
+
if tr['actual_properties']
|
304
|
+
toprops = JSON.parse(tr['actual_properties'])
|
305
|
+
end
|
306
|
+
|
307
|
+
|
308
|
+
z[k][propname] = {
|
309
|
+
'From' => toprops[propname],
|
310
|
+
'To' => fromprops[propname]
|
311
|
+
}
|
312
|
+
end
|
313
|
+
else
|
314
|
+
z = result[info['action']] ||= {}
|
315
|
+
|
316
|
+
z[k] = {}
|
317
|
+
end
|
318
|
+
|
319
|
+
end
|
320
|
+
|
321
|
+
result
|
322
|
+
|
323
|
+
end
|
324
|
+
|
147
325
|
def self.curtimestr
|
148
326
|
Time.now.strftime('%Y%m%d%H%M%S')
|
149
327
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sumomo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.10.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Siaw
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-06-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|