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.
@@ -1,10 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'momo'
2
4
  require 's3cabinet'
3
5
  require 'aws-sdk'
4
6
  require 'zip'
5
7
  require 'yaml'
6
8
 
7
- require "sumomo/version"
9
+ require 'sumomo/version'
8
10
  require 'sumomo/api'
9
11
  require 'sumomo/cdn'
10
12
  require 'sumomo/dns'
@@ -16,236 +18,236 @@ require 'sumomo/momo_extensions/resource'
16
18
  require 'sumomo/momo_extensions/stack'
17
19
 
18
20
  module Sumomo
19
-
20
- def self.make_master_key_name(name:)
21
- "#{name}_master_key"
22
- end
23
-
24
- def self.make_master_key_key(name:)
25
- "cloudformation/#{make_master_key_name(name: name)}.pem"
26
- end
27
-
28
- def self.update_stack(name:, region:, sns_arn:nil, &block)
29
-
30
- cf = Aws::CloudFormation::Client.new(region: region)
31
- s3 = Aws::S3::Client.new(region: region)
32
- ec2 = Aws::EC2::Client.new(region: region)
33
-
34
- begin
35
- s3.head_bucket(bucket: name)
36
- rescue Aws::S3::Errors::NotFound => e
37
- s3.create_bucket(bucket: name)
38
- end
39
-
40
- store = S3Cabinet::S3Cabinet.new(nil, nil, name, region)
41
-
42
- master_key_name = make_master_key_name(name: name)
43
- master_key_key = make_master_key_key(name: name)
44
-
45
- if !store.get(master_key_key)
46
-
47
- resp = nil
48
- begin
49
- puts "No master key found, creating..."
50
- resp = ec2.create_key_pair(key_name: master_key_name)
51
- rescue
52
- puts "Master key conflict! Deleting old one"
53
- ec2.delete_key_pair(key_name: master_key_name)
54
- resp = ec2.create_key_pair(key_name: master_key_name)
55
- end
56
-
57
- store.set(master_key_key, resp.key_material)
58
- store.set("#{master_key_key}.fingerprint", resp.key_fingerprint)
59
-
60
- end
61
-
62
- dummy_number = store.get("cloudformation/dummy_number")
63
- if dummy_number == nil
64
- dummy_number = 0
65
- end
66
- dummy_number += 1
67
- store.set("cloudformation/dummy_number", dummy_number)
68
-
69
- hidden_values = []
70
-
71
- template = Momo::cfl do
72
- inject Sumomo::Stack
73
-
74
- @region = region
75
- @version_number = dummy_number
76
- @custom_resources = {}
77
- @bucket_name = name
78
- @store = store
79
- @master_key_name = master_key_name
80
- @ec2 = ec2
81
- @cf = cf
82
- @s3 = s3
83
-
84
- make "AWS::EC2::SecurityGroup", name: "DummyResource" do
85
- GroupDescription "Dummy thing for Cloudformation Deployment."
86
- Tags [{"Key" => "Name", "Value" => "dummyfordeploy#{dummy_number}"}]
87
- end
88
-
89
- instance_eval(&block)
90
-
91
- hidden_values = @hidden_values
92
-
93
- end.templatize
94
-
95
- # TODO if the template is too big, split it into nested templates
96
-
97
- #puts JSON.parse(template).to_yaml
98
-
99
- store.set_raw("cloudformation/template", template)
100
-
101
- update_options = {
102
- stack_name: name,
103
- template_url: store.url("cloudformation/template"),
104
- parameters: hidden_values,
105
- capabilities: ["CAPABILITY_IAM"]
106
- }
107
-
108
- begin
109
- cf.update_stack(update_options)
110
- rescue => e
111
- if e.message.end_with? "does not exist"
112
- update_options[:timeout_in_minutes] = 30
113
- update_options[:notification_arns] = sns_arn if sns_arn
114
- cf.create_stack(update_options)
115
- else
116
- p e
117
- puts "Error: #{e.message}"
118
- end
119
- end
120
- end
121
-
122
- def self.wait_for_stack(name:, region:)
123
- cf = Aws::CloudFormation::Client.new(region: region)
124
-
125
- stack_id = name
126
-
127
- begin
128
- resp = cf.describe_stack_events(stack_name: stack_id)
129
- top_event = resp.stack_events[0]
130
- top_event_id = top_event.event_id
131
- puts "#{top_event.logical_resource_id} #{top_event.resource_status} #{top_event.resource_status_reason}"
132
- rescue => e
133
- puts "describe_stack_events: #{e.message}"
134
- end
135
-
136
- failure_count = 0
137
- loop do
138
- begin
139
- unless /^arn\:/.match(stack_id)
140
- stack_id = cf.describe_stacks(stack_name: stack_id).stacks[0].stack_id
141
- #puts "Unique Stack ID: #{stack_id}"
142
- end
143
-
144
- resp = cf.describe_stack_events(stack_name: stack_id)
145
- curr = 0
146
- lines = []
147
- loop do
148
- curr_event = resp.stack_events[curr]
149
- break if curr_event.event_id == top_event_id
150
- lines << "#{curr_event.logical_resource_id} #{curr_event.resource_status} #{curr_event.resource_status_reason}"
151
- break if curr == resp.stack_events.length - 1
152
- curr += 1
153
- end
154
-
155
- lines.reverse.each { |x| puts x }
156
-
157
- top_event_id = resp.stack_events[0].event_id
158
- rescue => e
159
- puts "describe_stack_events: #{e.message}"
160
- failure_count += 1
161
- break if failure_count > 5
162
- end
163
-
164
-
165
- sleep 1
166
- begin
167
- resp = cf.describe_stacks(stack_name: stack_id)
168
-
169
- break if /(COMPLETE$)|(FAILED$)/.match(resp.stacks[0].stack_status)
170
-
171
- rescue => e
172
- puts "describe_stacks: #{e.message}"
173
- break
174
- end
175
- end
176
- end
177
-
178
- class APITester
179
-
180
- attr_accessor :apis
181
- def initialize(&block)
182
- @apis = {}
183
- instance_eval(&block)
184
- end
185
-
186
- def make_api(domain_name, name:, script:nil, dns:nil, cert:nil, with_statements: [], &block)
187
- @apis[name] = block
188
- end
189
-
190
- def method_missing(name, *args, &block)
191
- end
192
- end
193
-
194
- def self.test_api(apiname, pretty_print, &block)
195
- tester = APITester.new(&block)
196
- test_name = nil
197
- if tester.apis.length == 1
198
- test_name = tester.apis.keys.first
199
- elsif apiname
200
- if tester.apis.has_key? apiname
201
- test_name = apiname
202
- else
203
- puts "Unknown API name. Please choose from one of the APIs: #{tester.apis.keys.inspect}"
204
- end
205
- else
206
- puts "Please choose from one of the APIs: #{tester.apis.keys.inspect}"
207
- end
208
-
209
- if test_name
210
- puts "Testing API #{test_name}"
211
- apigen = Stack::APIGenerator.new(pretty_print: pretty_print, &tester.apis[test_name])
212
-
213
- script = File.read(File.join(Gem.loaded_specs['sumomo'].full_gem_path, "data", "sumomo", "api_modules", "test_script.js"))
214
- script.sub!("// {{ ROUTES }}", apigen.generate);
215
- script.gsub!("{{ SCRIPT }}", apigen.init_script);
216
-
217
- File.write(".test.js", script)
218
- Stack::APIGenerator.combine_modules(".test_modules")
219
-
220
- exec "NODE_PATH=.test_modules node .test.js"
221
- end
222
- end
223
-
224
- def self.delete_stack(name:, region:, retain_bucket: false)
225
- cf = Aws::CloudFormation::Client.new(region: region)
226
- ec2 = Aws::EC2::Client.new(region: region)
227
-
228
- cf.delete_stack(stack_name: name)
229
- ec2.delete_key_pair(key_name: make_master_key_name(name: name))
230
-
231
- if !retain_bucket
232
- self.wait_for_stack(name: name, region: region)
233
- s3 = Aws::S3::Resource.new(region: region)
234
- bucket = s3.bucket(name)
235
- bucket.delete!
236
- end
237
- end
238
-
239
- def self.get_stack_outputs(name:, region:)
240
- cf = Aws::CloudFormation::Client.new(region: region)
241
-
242
- map = {}
243
- cf.describe_stacks(stack_name: name).stacks[0].outputs.each do |x|
244
- map[x.output_key] = x.output_value
245
- end
246
-
247
- map
248
- end
249
-
250
- singleton_class.send(:alias_method, :create_stack, :update_stack)
21
+ def self.make_master_key_name(name:)
22
+ "#{name}_master_key"
23
+ end
24
+
25
+ def self.make_master_key_key(name:)
26
+ "cloudformation/#{make_master_key_name(name: name)}.pem"
27
+ end
28
+
29
+ def self.update_stack(name:, region:, sns_arn: nil, &block)
30
+ cf = Aws::CloudFormation::Client.new(region: region)
31
+ s3 = Aws::S3::Client.new(region: region)
32
+ ec2 = Aws::EC2::Client.new(region: region)
33
+
34
+ begin
35
+ s3.head_bucket(bucket: name)
36
+ rescue Aws::S3::Errors::NotFound => e
37
+ s3.create_bucket(bucket: name)
38
+ end
39
+
40
+ store = S3Cabinet::S3Cabinet.new(nil, nil, name, region)
41
+
42
+ master_key_name = make_master_key_name(name: name)
43
+ master_key_key = make_master_key_key(name: name)
44
+
45
+ unless store.get(master_key_key)
46
+
47
+ resp = nil
48
+ begin
49
+ puts 'No master key found, creating...'
50
+ resp = ec2.create_key_pair(key_name: master_key_name)
51
+ rescue StandardError
52
+ puts 'Master key conflict! Deleting old one'
53
+ ec2.delete_key_pair(key_name: master_key_name)
54
+ resp = ec2.create_key_pair(key_name: master_key_name)
55
+ end
56
+
57
+ store.set(master_key_key, resp.key_material)
58
+ store.set("#{master_key_key}.fingerprint", resp.key_fingerprint)
59
+
60
+ end
61
+
62
+ dummy_number = store.get('cloudformation/dummy_number')
63
+ dummy_number = 0 if dummy_number.nil?
64
+ dummy_number += 1
65
+ store.set('cloudformation/dummy_number', dummy_number)
66
+
67
+ hidden_values = []
68
+
69
+ template = Momo.cfl do
70
+ inject Sumomo::Stack
71
+
72
+ @region = region
73
+ @version_number = dummy_number
74
+ @custom_resources = {}
75
+ @bucket_name = name
76
+ @store = store
77
+ @master_key_name = master_key_name
78
+ @ec2 = ec2
79
+ @cf = cf
80
+ @s3 = s3
81
+ @has_dummy = true
82
+ @dummy_vpc = nil
83
+
84
+ instance_eval(&block)
85
+
86
+ dummy_vpc = @dummy_vpc
87
+
88
+ if @has_dummy
89
+ make 'AWS::EC2::SecurityGroup', name: 'DummyResource' do
90
+ GroupDescription 'Dummy resource for tracking Cloudformation Deployment.'
91
+ VpcId dummy_vpc unless dummy_vpc.nil?
92
+ Tags [{ 'Key' => 'Name', 'Value' => "dummyfordeploy#{dummy_number}" }]
93
+ end
94
+ end
95
+
96
+ hidden_values = @hidden_values
97
+ end.templatize
98
+
99
+ # TODO: if the template is too big, split it into nested templates
100
+
101
+ # puts JSON.parse(template).to_yaml
102
+
103
+ store.set_raw('cloudformation/template', template)
104
+
105
+ update_options = {
106
+ stack_name: name,
107
+ template_url: store.url('cloudformation/template'),
108
+ parameters: hidden_values,
109
+ capabilities: ['CAPABILITY_IAM']
110
+ }
111
+
112
+ begin
113
+ cf.update_stack(update_options)
114
+ rescue StandardError => e
115
+ if e.message.end_with? 'does not exist'
116
+ update_options[:timeout_in_minutes] = 30
117
+ update_options[:notification_arns] = sns_arn if sns_arn
118
+ cf.create_stack(update_options)
119
+ else
120
+ p e
121
+ puts "Error: #{e.message}"
122
+ end
123
+ end
124
+ end
125
+
126
+ def self.wait_for_stack(name:, region:)
127
+ cf = Aws::CloudFormation::Client.new(region: region)
128
+
129
+ stack_id = name
130
+
131
+ begin
132
+ resp = cf.describe_stack_events(stack_name: stack_id)
133
+ top_event = resp.stack_events[0]
134
+ top_event_id = top_event.event_id
135
+ puts "#{top_event.logical_resource_id} #{top_event.resource_status} #{top_event.resource_status_reason}"
136
+ rescue StandardError => e
137
+ puts "describe_stack_events: #{e.message}"
138
+ end
139
+
140
+ failure_count = 0
141
+ loop do
142
+ begin
143
+ unless /^arn\:/.match(stack_id)
144
+ stack_id = cf.describe_stacks(stack_name: stack_id).stacks[0].stack_id
145
+ # puts "Unique Stack ID: #{stack_id}"
146
+ end
147
+
148
+ resp = cf.describe_stack_events(stack_name: stack_id)
149
+ curr = 0
150
+ lines = []
151
+ loop do
152
+ curr_event = resp.stack_events[curr]
153
+ break if curr_event.event_id == top_event_id
154
+
155
+ lines << "#{curr_event.logical_resource_id} #{curr_event.resource_status} #{curr_event.resource_status_reason}"
156
+ break if curr == resp.stack_events.length - 1
157
+
158
+ curr += 1
159
+ end
160
+
161
+ lines.reverse.each { |x| puts x }
162
+
163
+ top_event_id = resp.stack_events[0].event_id
164
+ rescue StandardError => e
165
+ puts "describe_stack_events: #{e.message}"
166
+ failure_count += 1
167
+ break if failure_count > 5
168
+ end
169
+
170
+ sleep 1
171
+ begin
172
+ resp = cf.describe_stacks(stack_name: stack_id)
173
+
174
+ break if /(COMPLETE$)|(FAILED$)/.match(resp.stacks[0].stack_status)
175
+ rescue StandardError => e
176
+ puts "describe_stacks: #{e.message}"
177
+ break
178
+ end
179
+ end
180
+ end
181
+
182
+ class APITester
183
+ attr_accessor :apis
184
+ def initialize(&block)
185
+ @apis = {}
186
+ instance_eval(&block)
187
+ end
188
+
189
+ def make_api(_domain_name, name:, script: nil, dns: nil, cert: nil, with_statements: [], &block)
190
+ @apis[name] = block
191
+ end
192
+
193
+ def method_missing(name, *args, &block); end
194
+ end
195
+
196
+ def self.test_api(apiname, pretty_print, &block)
197
+ tester = APITester.new(&block)
198
+ test_name = nil
199
+ if tester.apis.length == 1
200
+ test_name = tester.apis.keys.first
201
+ elsif apiname
202
+ if tester.apis.key? apiname
203
+ test_name = apiname
204
+ else
205
+ puts "Unknown API name. Please choose from one of the APIs: #{tester.apis.keys.inspect}"
206
+ end
207
+ else
208
+ puts "Please choose from one of the APIs: #{tester.apis.keys.inspect}"
209
+ end
210
+
211
+ if test_name
212
+ puts "Testing API #{test_name}"
213
+ apigen = Stack::APIGenerator.new(pretty_print: pretty_print, &tester.apis[test_name])
214
+
215
+ script = File.read(File.join(Gem.loaded_specs['sumomo'].full_gem_path, 'data', 'sumomo', 'api_modules', 'test_script.js'))
216
+ script.sub!('// {{ ROUTES }}', apigen.generate)
217
+ script.gsub!('{{ SCRIPT }}', apigen.init_script)
218
+
219
+ File.write('.test.js', script)
220
+ Stack::APIGenerator.combine_modules('.test_modules')
221
+
222
+ exec 'NODE_PATH=.test_modules node .test.js'
223
+ end
224
+ end
225
+
226
+ def self.delete_stack(name:, region:, retain_bucket: false)
227
+ cf = Aws::CloudFormation::Client.new(region: region)
228
+ ec2 = Aws::EC2::Client.new(region: region)
229
+
230
+ cf.delete_stack(stack_name: name)
231
+ ec2.delete_key_pair(key_name: make_master_key_name(name: name))
232
+
233
+ unless retain_bucket
234
+ wait_for_stack(name: name, region: region)
235
+ s3 = Aws::S3::Resource.new(region: region)
236
+ bucket = s3.bucket(name)
237
+ bucket.delete!
238
+ end
239
+ end
240
+
241
+ def self.get_stack_outputs(name:, region:)
242
+ cf = Aws::CloudFormation::Client.new(region: region)
243
+
244
+ map = {}
245
+ cf.describe_stacks(stack_name: name).stacks[0].outputs.each do |x|
246
+ map[x.output_key] = x.output_value
247
+ end
248
+
249
+ map
250
+ end
251
+
252
+ singleton_class.send(:alias_method, :create_stack, :update_stack)
251
253
  end