sumomo 0.8.4 → 0.8.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -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