eb_deployer 0.0.6 → 0.0.7

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.
@@ -0,0 +1,47 @@
1
+ module EbDeployer
2
+ class CloudFormationDriver
3
+
4
+ def stack_exists?(name)
5
+ stack(name).exists?
6
+ end
7
+
8
+ def create_stack(name, template, opts)
9
+ cloud_formation.stacks.create(name, tempalte, opts)
10
+ end
11
+
12
+ def update_stack(name, tempalte, opts)
13
+ begin
14
+ stack(name).update(opts.merge(:tempalte => template))
15
+ rescue AWS::CloudFormation::Errors::ValidationError => e
16
+ if e.message =~ /No updates are to be performed/
17
+ log(e.message)
18
+ else
19
+ raise
20
+ end
21
+ end
22
+ end
23
+
24
+ def stack_status(name)
25
+ stack(name).status.downcase.to_sym
26
+ end
27
+
28
+ def query_output(name, key)
29
+ stack(name).outputs.find { |o| o.key == key }.try(:value)
30
+ end
31
+
32
+ private
33
+
34
+ def cloud_formation
35
+ AWS::CloudFormation.new
36
+ end
37
+
38
+ def stack(name)
39
+ cloud_formation.stacks[name]
40
+ end
41
+
42
+ def log(msg)
43
+ puts "[#{Time.now.utc}][cloud_formation] #{msg}"
44
+ end
45
+ end
46
+
47
+ end
@@ -1,10 +1,14 @@
1
1
  module EbDeployer
2
+ class ResourceNotInReadyState < StandardError
3
+ end
4
+
2
5
  class CloudFormationProvisioner
3
6
  SUCCESS_STATS = [:create_complete, :update_complete, :update_rollback_complete]
4
7
  FAILED_STATS = [:create_failed, :update_failed]
5
8
 
6
- def initialize(stack_name)
9
+ def initialize(stack_name, cf_driver)
7
10
  @stack_name = stack_name
11
+ @cf_driver = cf_driver
8
12
  end
9
13
 
10
14
  def provision(resources)
@@ -12,35 +16,36 @@ module EbDeployer
12
16
  template = File.read(resources[:template])
13
17
  transforms = resources[:transforms]
14
18
 
15
- stack.exists? ? update_stack(template, params) : create_stack(template, params)
19
+ stack_exists? ? update_stack(template, params) : create_stack(template, params)
16
20
  wait_for_stack_op_terminate
17
21
  transform_output_to_settings(transforms)
18
22
  end
19
23
 
20
24
  def output(key)
21
- stack.outputs.find { |o| o.key == key }.try(:value)
25
+ @cf_driver.query_output(@stack_name, key)
26
+ rescue AWS::CloudFormation::Errors::ValidationError => e
27
+ raise ResourceNotInReadyState.new("Resource stack not in ready state yet, perhaps you should provision it first?")
22
28
  end
23
29
 
24
-
25
30
  private
26
31
 
27
32
  def update_stack(template, params)
28
- begin
29
- stack.update(:template => template, :parameters => params)
30
- rescue AWS::CloudFormation::Errors::ValidationError => e
31
- if e.message =~ /No updates are to be performed/
32
- log(e.message)
33
- else
34
- raise
35
- end
36
- end
33
+ @cf_driver.update_stack(@stack_name, template,
34
+ :parameters => params)
35
+ end
36
+
37
+ def stack_exists?
38
+ @cf_driver.stack_exists?(@stack_name)
37
39
  end
38
40
 
39
41
  def create_stack(template, params)
40
- cloud_formation.stacks.create(@stack_name, template, {
41
- :disable_rollback => true,
42
- :parameters => params
43
- })
42
+ @cf_driver.create_stack(@stack_name, template,
43
+ :disable_rollback => true,
44
+ :parameters => params)
45
+ end
46
+
47
+ def stack_status
48
+ @cf_driver.stack_status(@stack_name)
44
49
  end
45
50
 
46
51
  def transform_output_to_settings(transforms)
@@ -52,12 +57,13 @@ module EbDeployer
52
57
  end
53
58
 
54
59
  def wait_for_stack_op_terminate
55
- begin
60
+ stats = stack_status
61
+ while !SUCCESS_STATS.include?(stats)
56
62
  sleep 15
57
63
  stats = stack_status
58
64
  raise "Resource stack update failed!" if FAILED_STATS.include?(stats)
59
65
  log "current status: #{stack_status}"
60
- end while !SUCCESS_STATS.include?(stats)
66
+ end
61
67
  end
62
68
 
63
69
 
@@ -68,17 +74,5 @@ module EbDeployer
68
74
  def log(msg)
69
75
  puts "[#{Time.now.utc}][resources-stack] #{msg}"
70
76
  end
71
-
72
- def stack_status
73
- stack.status.downcase.to_sym
74
- end
75
-
76
- def stack
77
- cloud_formation.stacks[@stack_name]
78
- end
79
-
80
- def cloud_formation
81
- AWS::CloudFormation.new
82
- end
83
77
  end
84
78
  end
@@ -1,3 +1,3 @@
1
1
  module EbDeployer
2
- VERSION = "0.0.6"
2
+ VERSION = "0.0.7"
3
3
  end
data/lib/eb_deployer.rb CHANGED
@@ -6,6 +6,7 @@ require "eb_deployer/environment"
6
6
  require "eb_deployer/event_poller"
7
7
  require "eb_deployer/package"
8
8
  require 'eb_deployer/s3_driver'
9
+ require 'eb_deployer/cloud_formation_driver'
9
10
  require 'digest'
10
11
  require 'set'
11
12
  require 'time'
@@ -21,8 +22,9 @@ module EbDeployer
21
22
  end
22
23
  app = opts[:application]
23
24
  env_name = opts[:environment]
24
- cf = CloudFormationProvisioner.new("#{app}-#{env_name}")
25
- cf.output(key)
25
+ cf = opts[:cf_driver] || CloudFormationDriver.new
26
+ provisioner = CloudFormationProvisioner.new("#{app}-#{env_name}", cf)
27
+ provisioner.output(key)
26
28
  end
27
29
 
28
30
  def self.deploy(opts)
@@ -33,6 +35,7 @@ module EbDeployer
33
35
 
34
36
  bs = opts[:bs_driver] || Beanstalk.new
35
37
  s3 = opts[:s3_driver] || S3Driver.new
38
+ cf = opts[:cf_driver] || CloudFormationDriver.new
36
39
  stack_name = opts[:solution_stack_name] || "64bit Amazon Linux running Tomcat 7"
37
40
  app = opts[:application]
38
41
  env_name = opts[:environment]
@@ -44,7 +47,7 @@ module EbDeployer
44
47
  smoke_test = opts[:smoke_test] || Proc.new {}
45
48
 
46
49
  package = Package.new(opts[:package], app + "-packages", s3)
47
- cf = CloudFormationProvisioner.new("#{app}-#{env_name}")
50
+ cf = CloudFormationProvisioner.new("#{app}-#{env_name}", cf)
48
51
  strategy = DeploymentStrategy.create(strategy_name, app, env_name, bs,
49
52
  :solution_stack => stack_name,
50
53
  :cname_prefix => cname_prefix,
@@ -64,6 +64,10 @@ class EBStub
64
64
  @envs[env_key(app_name, env_name)][:version]
65
65
  end
66
66
 
67
+ def environment_settings(app_name, env_name)
68
+ @envs[env_key(app_name, env_name)][:settings]
69
+ end
70
+
67
71
  private
68
72
  def env_key(app, name)
69
73
  [app, name].join("-")
@@ -92,3 +96,30 @@ class S3Stub
92
96
  @buckets[bucket_name][obj_name] = file
93
97
  end
94
98
  end
99
+
100
+ class CFStub
101
+ def initialize
102
+ @stacks = {}
103
+ end
104
+
105
+ def create_stack(name, template, opts)
106
+ @stacks[name] = {:template => template, :opts => opts }
107
+ end
108
+
109
+ def update_stack(name, template, opts)
110
+ @stacks[name] = @stacks[name].merge(:template => template, opts => opts)
111
+ end
112
+
113
+ def stack_status(name)
114
+ @stacks[name] && :update_complete
115
+ end
116
+
117
+ def stack_exists?(name)
118
+ @stacks.has_key?(name)
119
+ end
120
+
121
+ def query_output(name, key)
122
+ raise AWS::CloudFormation::Errors::ValidationError.new unless stack_exists?(name)
123
+ "value of #{key}"
124
+ end
125
+ end
data/test/deploy_test.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require 'tempfile'
1
2
  require 'eb_deployer'
2
3
  require 'aws_driver_stubs'
3
4
  require 'minitest/autorun'
@@ -6,18 +7,11 @@ class DeployTest < Minitest::Test
6
7
  def setup
7
8
  @eb_driver = EBStub.new
8
9
  @s3_driver = S3Stub.new
10
+ @cf_driver = CFStub.new
9
11
  @sample_package = '/tmp/app-package.war'
10
12
  File.open(@sample_package, 'w') { |f| f << 's' * 100 }
11
13
  end
12
14
 
13
- def deploy(opts)
14
- EbDeployer.deploy({:package => @sample_package,
15
- :bs_driver => @eb_driver,
16
- :s3_driver => @s3_driver,
17
- :version_label => 1}.merge(opts))
18
- end
19
-
20
-
21
15
  def test_first_deployment_create_environment
22
16
  assert !@eb_driver.environment_exists?('simple', eb_envname('simple', 'production'))
23
17
  deploy(:application => 'simple', :environment => "production")
@@ -152,9 +146,91 @@ class DeployTest < Minitest::Test
152
146
  'simple-production-inactive.elasticbeanstalk.com'], smoked_host
153
147
  end
154
148
 
149
+ def test_deploy_with_resources_declared_will_create_a_cf_stack_for_env
150
+ cf_template = temp_file(JSON.dump({'Resources' => {'R1' => {}}}))
151
+ deploy(:application => 'simple', :environment => "production",
152
+ :resources => {
153
+ :template => cf_template
154
+ })
155
+ assert @cf_driver.stack_exists?('simple-production')
156
+ end
157
+
158
+ def test_transforms_resource_provsion_output_to_elastic_beanstalk_settings
159
+ cf_template = temp_file(JSON.dump({'Resources' => {'R1' => {}}}))
160
+ deploy(:application => 'simple', :environment => "production",
161
+ :resources => {
162
+ :template => cf_template
163
+ })
164
+ assert @cf_driver.stack_exists?('simple-production')
165
+ end
166
+
167
+ def test_transforms_resource_provsion_output_to_elastic_beanstalk_settings
168
+ cf_template = temp_file(JSON.dump({
169
+ 'Resources' => {'R1' => {}},
170
+ 'Outputs' => {'O1' => {}, 'O2' => {}}
171
+ }))
172
+ deploy(:application => 'simple', :environment => "production",
173
+ :resources => {
174
+ :template => cf_template,
175
+ :transforms => {
176
+ 'O1' => lambda { |v| {:namespace => 'aws.foo', :option_name => 'o1', :value => v} }
177
+ }
178
+ })
179
+ assert @eb_driver.environment_settings('simple', eb_envname('simple', 'production')).
180
+ include?({:namespace => 'aws.foo', :option_name => 'o1', :value => 'value of O1'})
181
+ end
182
+
183
+ def test_can_query_resource_stack_output_after_deploy
184
+ cf_template = temp_file(JSON.dump({
185
+ 'Resources' => {'R1' => {}},
186
+ 'Outputs' => {'O1' => {}, 'O2' => {}}
187
+ }))
188
+ deploy(:application => 'simple',
189
+ :environment => "production",
190
+ :resources => { :template => cf_template })
191
+ assert_equal 'value of O1', query_resource_output('O1',
192
+ :application => 'simple',
193
+ :environment => "production")
194
+ assert_equal 'value of O2', query_resource_output('O2',
195
+ :application => 'simple',
196
+ :environment => "production")
197
+
198
+ end
199
+
200
+ def test_should_raise_error_if_query_resources_that_have_not_been_provisioned_yet
201
+ assert_raises(EbDeployer::ResourceNotInReadyState) do
202
+ query_resource_output('O1',
203
+ :application => 'simple',
204
+ :environment => "production")
205
+ end
206
+ end
207
+
155
208
  private
156
209
 
210
+ def temp_file(content)
211
+ f = Tempfile.new("foo")
212
+ f.write(content)
213
+ f
214
+ end
215
+
157
216
  def eb_envname(app_name, env_name)
158
217
  EbDeployer::Environment.unique_ebenv_name(app_name, env_name)
159
218
  end
219
+
220
+ def query_resource_output(key, opts)
221
+ EbDeployer.query_resource_output(key, {:bs_driver => @eb_driver,
222
+ :s3_driver => @s3_driver,
223
+ :cf_driver => @cf_driver}.merge(opts))
224
+ end
225
+
226
+ def deploy(opts)
227
+ EbDeployer.deploy({:package => @sample_package,
228
+ :bs_driver => @eb_driver,
229
+ :s3_driver => @s3_driver,
230
+ :cf_driver => @cf_driver,
231
+ :version_label => 1}.merge(opts))
232
+ end
233
+
234
+
235
+
160
236
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: eb_deployer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 0.0.7
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -60,6 +60,7 @@ files:
60
60
  - eb_deployer.gemspec
61
61
  - lib/eb_deployer.rb
62
62
  - lib/eb_deployer/beanstalk.rb
63
+ - lib/eb_deployer/cloud_formation_driver.rb
63
64
  - lib/eb_deployer/cloud_formation_provisioner.rb
64
65
  - lib/eb_deployer/deployment_strategy.rb
65
66
  - lib/eb_deployer/environment.rb