eb_deployer 0.0.6 → 0.0.7

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