sumomo 0.8.4 → 0.8.6
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 +5 -5
- data/.rubocop.yml +1 -0
- data/.rubocop_todo.yml +169 -0
- data/Gemfile +2 -0
- data/Rakefile +5 -3
- data/bin/console +4 -3
- data/data/sumomo/api_modules/node_modules/.bin/uuid +1 -1
- data/data/sumomo/custom_resources/TempS3Bucket.js +208 -0
- data/exe/sumomo +42 -41
- data/lib/sumomo.rb +235 -233
- data/lib/sumomo/api.rb +148 -151
- data/lib/sumomo/cdn.rb +119 -113
- data/lib/sumomo/dns.rb +20 -20
- data/lib/sumomo/ec2.rb +490 -491
- data/lib/sumomo/ecs.rb +256 -262
- data/lib/sumomo/irregular.rb +9 -0
- data/lib/sumomo/momo_extensions/resource.rb +8 -7
- data/lib/sumomo/momo_extensions/stack.rb +4 -3
- data/lib/sumomo/network.rb +109 -106
- data/lib/sumomo/stack.rb +191 -189
- data/lib/sumomo/version.rb +3 -1
- data/sumomo.gemspec +23 -22
- metadata +25 -22
@@ -1,10 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
|
2
3
|
module Momo
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
4
|
+
class Resource
|
5
|
+
def exec_role
|
6
|
+
res = Momo::Resource.new('AWS::IAM::Role', 'LambdaFunctionExecutionRole', @stack)
|
7
|
+
res.complete!
|
8
|
+
res
|
9
|
+
end
|
10
|
+
end
|
10
11
|
end
|
data/lib/sumomo/network.rb
CHANGED
@@ -1,108 +1,111 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support/inflector'
|
4
|
+
require 'hashie'
|
3
5
|
|
4
6
|
module Sumomo
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
end
|
7
|
+
module Stack
|
8
|
+
def make_network(layers: [])
|
9
|
+
zones = get_azs
|
10
|
+
|
11
|
+
region = @region
|
12
|
+
|
13
|
+
vpc = make 'AWS::EC2::VPC' do
|
14
|
+
CidrBlock '10.0.0.0/16'
|
15
|
+
EnableDnsSupport true
|
16
|
+
EnableDnsHostnames true
|
17
|
+
tag 'Name', call('Fn::Join', '-', [ref('AWS::StackName')])
|
18
|
+
end
|
19
|
+
|
20
|
+
gateway = make 'AWS::EC2::InternetGateway' do
|
21
|
+
tag 'Name', call('Fn::Join', '-', [ref('AWS::StackName')])
|
22
|
+
end
|
23
|
+
|
24
|
+
attachment = make 'AWS::EC2::VPCGatewayAttachment' do
|
25
|
+
VpcId vpc
|
26
|
+
InternetGatewayId gateway
|
27
|
+
end
|
28
|
+
|
29
|
+
inet_route_table = make 'AWS::EC2::RouteTable' do
|
30
|
+
depends_on attachment
|
31
|
+
VpcId vpc
|
32
|
+
tag 'Name', call('Fn::Join', '-', ['public', ref('AWS::StackName')])
|
33
|
+
end
|
34
|
+
|
35
|
+
make 'AWS::EC2::Route' do
|
36
|
+
RouteTableId inet_route_table
|
37
|
+
DestinationCidrBlock '0.0.0.0/0'
|
38
|
+
GatewayId gateway
|
39
|
+
end
|
40
|
+
|
41
|
+
last_unused_number = 0
|
42
|
+
|
43
|
+
subnet_numbers = []
|
44
|
+
|
45
|
+
# load current config
|
46
|
+
number_hash = {}
|
47
|
+
subnet_hash = {}
|
48
|
+
|
49
|
+
ec2 = Aws::EC2::Client.new(region: @region)
|
50
|
+
ec2_subnets = ec2.describe_subnets.subnets
|
51
|
+
ec2_subnets.each do |subnet|
|
52
|
+
unless subnet.tags.select { |x| x.key == 'aws:cloudformation:stack-name' && x.value == @bucket_name }.length == 1
|
53
|
+
next
|
54
|
+
end
|
55
|
+
|
56
|
+
layer = /^#{@bucket_name}-(?<layer_name>.+)-[a-z]+$/.match(subnet.tags.select { |x| x.key == 'Name' }.first.value)[:layer_name]
|
57
|
+
zone = subnet.availability_zone
|
58
|
+
number = /^10.0.(?<num>[0-9]+).0/.match(subnet.cidr_block)[:num].to_i
|
59
|
+
|
60
|
+
key = "#{layer}/#{zone}"
|
61
|
+
number_hash[number] = key
|
62
|
+
subnet_hash[key] = number
|
63
|
+
end
|
64
|
+
|
65
|
+
# assign numbers to unassigned subnets
|
66
|
+
layers.product(zones).each do |e|
|
67
|
+
key = "#{e[0]}/#{e[1]}"
|
68
|
+
if !subnet_hash.key?(key)
|
69
|
+
loop do
|
70
|
+
break unless number_hash.key?(last_unused_number)
|
71
|
+
|
72
|
+
last_unused_number += 1
|
73
|
+
end
|
74
|
+
number_hash[last_unused_number] = key
|
75
|
+
subnet_hash[key] = last_unused_number
|
76
|
+
subnet_numbers << [e, last_unused_number]
|
77
|
+
else
|
78
|
+
subnet_numbers << [e, subnet_hash[key]]
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
subnets = {}
|
83
|
+
|
84
|
+
subnet_numbers.each do |e, subnet_number|
|
85
|
+
layer = e[0]
|
86
|
+
zone = e[1]
|
87
|
+
|
88
|
+
zone_letter = zone.sub(region.to_s, '')
|
89
|
+
cidr = "10.0.#{subnet_number}.0/24"
|
90
|
+
|
91
|
+
subnet = make 'AWS::EC2::Subnet', name: "SubnetFor#{layer.camelize}Layer#{zone_letter.upcase}" do
|
92
|
+
AvailabilityZone zone
|
93
|
+
VpcId vpc
|
94
|
+
CidrBlock cidr
|
95
|
+
|
96
|
+
tag('Name', call('Fn::Join', '-', [ref('AWS::StackName'), layer.to_s, zone_letter]))
|
97
|
+
end
|
98
|
+
|
99
|
+
make 'AWS::EC2::SubnetRouteTableAssociation', name: "SubnetRTAFor#{layer.camelize}Layer#{zone_letter.upcase}" do
|
100
|
+
SubnetId subnet
|
101
|
+
RouteTableId inet_route_table
|
102
|
+
end
|
103
|
+
|
104
|
+
subnets[layer] ||= []
|
105
|
+
subnets[layer] << { name: subnet, cidr: cidr, zone: zone }
|
106
|
+
end
|
107
|
+
|
108
|
+
Hashie::Mash.new vpc: vpc, subnets: subnets, azs: zones, attachment: attachment
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
data/lib/sumomo/stack.rb
CHANGED
@@ -1,192 +1,194 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
|
2
3
|
module Sumomo
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
4
|
+
module Stack
|
5
|
+
def hidden_value(value)
|
6
|
+
name = make_default_resource_name('HiddenValue')
|
7
|
+
@hidden_values ||= []
|
8
|
+
|
9
|
+
@hidden_values << {
|
10
|
+
parameter_key: name,
|
11
|
+
parameter_value: value
|
12
|
+
}
|
13
|
+
|
14
|
+
param name, type: :string
|
15
|
+
end
|
16
|
+
|
17
|
+
def upload_file(name, content)
|
18
|
+
@store.set_raw("uploads/#{name}", content)
|
19
|
+
puts "Uploaded #{name}"
|
20
|
+
end
|
21
|
+
|
22
|
+
def make_lambda(name: nil, files: [{ name: 'index.js', code: '' }],
|
23
|
+
description: "Lambda Function in #{@bucket_name}",
|
24
|
+
function_key: "cloudformation/lambda/function_#{name}",
|
25
|
+
handler: 'index.handler',
|
26
|
+
runtime: 'nodejs8.10',
|
27
|
+
memory_size: 128,
|
28
|
+
timeout: 30,
|
29
|
+
role: nil)
|
30
|
+
|
31
|
+
name ||= make_default_resource_name('Lambda')
|
32
|
+
role ||= custom_resource_exec_role
|
33
|
+
|
34
|
+
stringio = Zip::OutputStream.write_buffer do |zio|
|
35
|
+
files.each do |file|
|
36
|
+
zio.put_next_entry(file[:name])
|
37
|
+
if file[:code]
|
38
|
+
zio.write file[:code]
|
39
|
+
elsif file[:path]
|
40
|
+
zio.write File.read(file[:path])
|
41
|
+
else
|
42
|
+
raise 'Files needs to be an array of objects with :name and :code or :path members'
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
@store.set_raw(function_key, stringio.string)
|
48
|
+
|
49
|
+
stack = self
|
50
|
+
|
51
|
+
code_location = { "S3Bucket": @bucket_name, "S3Key": function_key }
|
52
|
+
fun = make 'AWS::Lambda::Function', name: name do
|
53
|
+
Code code_location
|
54
|
+
Description description
|
55
|
+
MemorySize memory_size
|
56
|
+
Handler handler
|
57
|
+
Runtime runtime
|
58
|
+
Timeout timeout
|
59
|
+
Role role.Arn
|
60
|
+
end
|
61
|
+
|
62
|
+
log_group = make 'AWS::Logs::LogGroup', name: "#{name}LogGroup" do
|
63
|
+
LogGroupName call('Fn::Join', '', ['/aws/lambda/', fun])
|
64
|
+
RetentionInDays 30
|
65
|
+
end
|
66
|
+
|
67
|
+
fun
|
68
|
+
end
|
69
|
+
|
70
|
+
def define_custom_resource(name: nil, code:, role: nil)
|
71
|
+
name ||= make_default_resource_name('CustomResource')
|
72
|
+
role ||= custom_resource_exec_role
|
73
|
+
|
74
|
+
func = make_lambda(
|
75
|
+
name: name,
|
76
|
+
role: role,
|
77
|
+
files: [
|
78
|
+
{
|
79
|
+
name: 'index.js',
|
80
|
+
code: File.read(File.join(Gem.loaded_specs['sumomo'].full_gem_path, 'data', 'sumomo', 'custom_resource_utils.js')).sub('{{ CODE }}', code)
|
81
|
+
}
|
82
|
+
],
|
83
|
+
description: "CF Resource Custom::#{name}",
|
84
|
+
function_key: "cloudformation/custom_resources/function_#{name}"
|
85
|
+
)
|
86
|
+
|
87
|
+
@custom_resources["Custom::#{name}"] = func
|
88
|
+
end
|
89
|
+
|
90
|
+
def make_custom(custom_resource, options = {}, &block)
|
91
|
+
bucket_name = @bucket_name
|
92
|
+
stack_make "Custom::#{custom_resource.name}", options do
|
93
|
+
ServiceToken custom_resource.Arn
|
94
|
+
Region ref('AWS::Region')
|
95
|
+
Bucket bucket_name
|
96
|
+
instance_eval(&block) if block
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def make(type, options = {}, &block)
|
101
|
+
match = /^Custom\:\:(?<name>[a-z0-9]+)/i.match(type)
|
102
|
+
if match
|
103
|
+
unless @custom_resources[type]
|
104
|
+
|
105
|
+
resource_function_source = File.join(Gem.loaded_specs['sumomo'].full_gem_path, 'data', 'sumomo', 'custom_resources', "#{match[:name]}.js")
|
106
|
+
|
107
|
+
if File.exist? resource_function_source
|
108
|
+
define_custom_resource(name: match[:name], code: File.read(resource_function_source))
|
109
|
+
else
|
110
|
+
throw "#{resource_function_source} does not exist"
|
111
|
+
|
112
|
+
end
|
113
|
+
end
|
114
|
+
make_custom(@custom_resources[type], options, &block)
|
115
|
+
else
|
116
|
+
stack_make(type, options, &block)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def lambda_exec_role(statements: [], principals: [])
|
121
|
+
name = make_default_resource_name('LambdaExecRole')
|
122
|
+
|
123
|
+
role_policy_doc = {
|
124
|
+
'Version' => '2012-10-17',
|
125
|
+
'Statement' => [{
|
126
|
+
'Effect' => 'Allow',
|
127
|
+
'Principal' => { 'Service' => principals },
|
128
|
+
'Action' => ['sts:AssumeRole']
|
129
|
+
}]
|
130
|
+
}
|
131
|
+
|
132
|
+
make 'AWS::IAM::Role', name: name do
|
133
|
+
AssumeRolePolicyDocument role_policy_doc
|
134
|
+
Path '/'
|
135
|
+
Policies [
|
136
|
+
{
|
137
|
+
'PolicyName' => name,
|
138
|
+
'PolicyDocument' => {
|
139
|
+
'Version' => '2012-10-17',
|
140
|
+
'Statement' => statements
|
141
|
+
}
|
142
|
+
}
|
143
|
+
]
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def custom_resource_exec_role(with_statements: [])
|
148
|
+
@exec_roles ||= {}
|
149
|
+
|
150
|
+
statement_key = JSON.parse(with_statements.to_json)
|
151
|
+
|
152
|
+
@exec_roles[statement_key] ||= lambda_exec_role(
|
153
|
+
principals: ['edgelambda.amazonaws.com', 'lambda.amazonaws.com'],
|
154
|
+
statements: [
|
155
|
+
{
|
156
|
+
'Effect' => 'Allow',
|
157
|
+
'Action' => ['logs:CreateLogStream', 'logs:PutLogEvents'],
|
158
|
+
'Resource' => 'arn:aws:logs:*:*:*'
|
159
|
+
},
|
160
|
+
{
|
161
|
+
'Effect' => 'Allow',
|
162
|
+
'Action' => ['cloudformation:DescribeStacks', 'ec2:Describe*'],
|
163
|
+
'Resource' => '*'
|
164
|
+
},
|
165
|
+
{
|
166
|
+
'Effect' => 'Allow',
|
167
|
+
'Action' => ['s3:DeleteObject', 's3:GetObject', 's3:PutObject'],
|
168
|
+
'Resource' => "arn:aws:s3:::#{@bucket_name}/*"
|
169
|
+
},
|
170
|
+
{
|
171
|
+
'Effect' => 'Allow',
|
172
|
+
'Action' => ['cloudfront:CreateCloudFrontOriginAccessIdentity', 'cloudfront:DeleteCloudFrontOriginAccessIdentity'],
|
173
|
+
'Resource' => '*'
|
174
|
+
},
|
175
|
+
{
|
176
|
+
'Effect' => 'Allow',
|
177
|
+
'Action' => ['apigateway:*', 'cloudfront:UpdateDistribution'],
|
178
|
+
'Resource' => '*'
|
179
|
+
},
|
180
|
+
{
|
181
|
+
'Effect' => 'Allow',
|
182
|
+
'Action' => ['acm:RequestCertificate', 'acm:DeleteCertificate', 'acm:DescribeCertificate'],
|
183
|
+
'Resource' => '*'
|
184
|
+
},
|
185
|
+
{
|
186
|
+
'Effect' => 'Allow',
|
187
|
+
'Action' => ['s3:*'],
|
188
|
+
'Resource' => 'arn:aws:s3:::*'
|
189
|
+
}
|
190
|
+
] + with_statements
|
191
|
+
)
|
192
|
+
end
|
193
|
+
end
|
192
194
|
end
|