eb_deployer 0.3.6 → 0.3.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.
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 1.9.3-p429
1
+ 1.9.3-p484
data/.travis.yml ADDED
@@ -0,0 +1,10 @@
1
+ rvm:
2
+ - 1.9.3
3
+ - 2.0.0
4
+ - 2.1.0
5
+ - jruby-1.7.10
6
+ script: "bundle exec rake"
7
+ bundler_args: "--without repl documentation"
8
+ branches:
9
+ only:
10
+ - master
data/Gemfile CHANGED
@@ -5,4 +5,4 @@ gemspec
5
5
 
6
6
  gem 'redcarpet'
7
7
  gem 'yard'
8
-
8
+ gem 'rake'
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # EbDeployer
1
+ # EbDeployer [![Build Status](https://travis-ci.org/ThoughtWorksStudios/eb_deployer.png?branch=master)](https://travis-ci.org/ThoughtWorksStudios/eb_deployer)
2
2
 
3
3
  Low friction deployments should be a breeze. Elastic Beanstalk provides a great foundation for performing Blue-Green deployments, and EbDeployer add a missing link to automate the whole flow out of box.
4
4
 
@@ -1,5 +1,7 @@
1
1
  module EbDeployer
2
2
  class Application
3
+ attr_reader :name
4
+
3
5
  def initialize(name, eb_driver, s3_driver, bucket = nil)
4
6
  @name = name
5
7
  @eb_driver = eb_driver
@@ -34,7 +36,7 @@ module EbDeployer
34
36
 
35
37
  def delete(env_name=nil)
36
38
  if @eb_driver.application_exists?(@name)
37
- env_name = Environment.unique_ebenv_name(@name, env_name) unless env_name.nil?
39
+ env_name = EbEnvironment.unique_ebenv_name(@name, env_name) unless env_name.nil?
38
40
  available_envs = @eb_driver.environment_names_for_application(@name)
39
41
 
40
42
  unless env_name.nil? || available_envs.include?(env_name)
@@ -75,6 +77,10 @@ module EbDeployer
75
77
  end
76
78
  end
77
79
 
80
+ def clean_versions(version_prefix, retentions)
81
+ VersionCleaner.new(self, retentions).clean(version_prefix)
82
+ end
83
+
78
84
  private
79
85
 
80
86
  def create_application_if_not_exists
@@ -0,0 +1,127 @@
1
+ module EbDeployer
2
+ module AWSDriver
3
+ class Beanstalk
4
+ attr_reader :client
5
+
6
+ def initialize(client=AWS::ElasticBeanstalk.new.client)
7
+ @client = client
8
+ end
9
+
10
+ def create_application(app)
11
+ @client.create_application(:application_name => app)
12
+ end
13
+
14
+ def delete_application(app)
15
+ @client.delete_application(:application_name => app)
16
+ end
17
+
18
+ def application_exists?(app)
19
+ @client.describe_applications(:application_names => [app])[:applications].any?
20
+ end
21
+
22
+ def update_environment(app_name, env_name, version, tier, settings)
23
+ env_id = convert_env_name_to_id(app_name, [env_name]).first
24
+ request = {
25
+ :environment_id => env_id,
26
+ :version_label => version,
27
+ :option_settings => settings,
28
+ :tier => tier
29
+ }
30
+
31
+ @client.update_environment(request)
32
+ end
33
+
34
+ def environment_exists?(app_name, env_name)
35
+ alive_envs(app_name, [env_name]).any?
36
+ end
37
+
38
+ def environment_names_for_application(app_name)
39
+ alive_envs(app_name).collect { |env| env[:environment_name] }
40
+ end
41
+
42
+ def create_environment(app_name, env_name, stack_name, cname_prefix, version, tier, settings)
43
+ request = {
44
+ :application_name => app_name,
45
+ :environment_name => env_name,
46
+ :solution_stack_name => stack_name,
47
+ :version_label => version,
48
+ :option_settings => settings,
49
+ :tier => tier,
50
+ :cname_prefix => cname_prefix
51
+ }
52
+
53
+ @client.create_environment(request)
54
+ end
55
+
56
+ def delete_environment(app_name, env_name)
57
+ @client.terminate_environment(:environment_name => env_name)
58
+ end
59
+
60
+ def delete_application_version(app_name, version, delete_source_bundle)
61
+ request = {
62
+ :application_name => app_name,
63
+ :version_label => version,
64
+ :delete_source_bundle => delete_source_bundle
65
+ }
66
+ @client.delete_application_version(request)
67
+ end
68
+
69
+ def create_application_version(app_name, version_label, source_bundle)
70
+ @client.create_application_version(:application_name => app_name,
71
+ :source_bundle => source_bundle,
72
+ :version_label => version_label)
73
+ end
74
+
75
+ def application_version_labels(app_name)
76
+ application_versions(app_name).map { |apv| apv[:version_label] }
77
+ end
78
+
79
+ def application_versions(app_name)
80
+ request = { :application_name => app_name }
81
+ @client.describe_application_versions(request)[:application_versions]
82
+ end
83
+
84
+ def fetch_events(app_name, env_name, params, &block)
85
+ response = @client.describe_events(params.merge(:application_name => app_name,
86
+ :environment_name => env_name))
87
+ return [response[:events], response[:next_token]]
88
+ end
89
+
90
+ def environment_cname_prefix(app_name, env_name)
91
+ cname = environment_cname(app_name, env_name)
92
+ if cname =~ /^(.+)\.elasticbeanstalk\.com/
93
+ $1
94
+ end
95
+ end
96
+
97
+ def environment_cname(app_name, env_name)
98
+ env = alive_envs(app_name, [env_name]).first
99
+ env && env[:cname]
100
+ end
101
+
102
+ def environment_health_state(app_name, env_name)
103
+ env = alive_envs(app_name, [env_name]).first
104
+ env && env[:health]
105
+ end
106
+
107
+ def environment_swap_cname(app_name, env1, env2)
108
+ env1_id, env2_id = convert_env_name_to_id(app_name, [env1, env2])
109
+ @client.swap_environment_cnam_es(:source_environment_id => env1_id,
110
+ :destination_environment_id => env2_id)
111
+ end
112
+
113
+ private
114
+
115
+ def convert_env_name_to_id(app_name, env_names)
116
+ envs = alive_envs(app_name, env_names)
117
+ envs.map { |env| env[:environment_id] }
118
+ end
119
+
120
+ def alive_envs(app_name, env_names=[])
121
+ envs = @client.describe_environments(:application_name => app_name, :environment_names => env_names)[:environments]
122
+
123
+ envs.select {|e| e[:status] != 'Terminated' }
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,49 @@
1
+ module EbDeployer
2
+ module AWSDriver
3
+ class CloudFormationDriver
4
+
5
+ def stack_exists?(name)
6
+ stack(name).exists?
7
+ end
8
+
9
+ def create_stack(name, template, opts)
10
+ cloud_formation.stacks.create(name, template, opts)
11
+ end
12
+
13
+ def update_stack(name, template, opts)
14
+ begin
15
+ stack(name).update(opts.merge(:template => template))
16
+ rescue AWS::CloudFormation::Errors::ValidationError => e
17
+ if e.message =~ /No updates are to be performed/
18
+ log(e.message)
19
+ else
20
+ raise
21
+ end
22
+ end
23
+ end
24
+
25
+ def stack_status(name)
26
+ stack(name).status.downcase.to_sym
27
+ end
28
+
29
+ def query_output(name, key)
30
+ output = stack(name).outputs.find { |o| o.key == key }
31
+ output && output.value
32
+ end
33
+
34
+ private
35
+
36
+ def cloud_formation
37
+ AWS::CloudFormation.new
38
+ end
39
+
40
+ def stack(name)
41
+ cloud_formation.stacks[name]
42
+ end
43
+
44
+ def log(msg)
45
+ puts "[#{Time.now.utc}][cloud_formation_driver] #{msg}"
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,35 @@
1
+ module EbDeployer
2
+ module AWSDriver
3
+ class S3Driver
4
+ def create_bucket(bucket_name)
5
+ buckets.create(bucket_name)
6
+ end
7
+
8
+ def bucket_exists?(bucket_name)
9
+ buckets[bucket_name].exists?
10
+ end
11
+
12
+ def object_length(bucket_name, obj_name)
13
+ obj(bucket_name, obj_name).content_length rescue nil
14
+ end
15
+
16
+ def upload_file(bucket_name, obj_name, file)
17
+ o = obj(bucket_name, obj_name)
18
+ File.open(file) { |f| o.write(f) }
19
+ end
20
+
21
+ private
22
+ def s3
23
+ AWS::S3.new
24
+ end
25
+
26
+ def obj(bucket_name, obj_name)
27
+ buckets[bucket_name].objects[obj_name]
28
+ end
29
+
30
+ def buckets
31
+ s3.buckets
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,8 @@
1
+ require 'eb_deployer/aws_driver/s3_driver'
2
+ require 'eb_deployer/aws_driver/beanstalk'
3
+ require 'eb_deployer/aws_driver/cloud_formation_driver'
4
+
5
+ module EbDeployer
6
+ module AWSDriver
7
+ end
8
+ end
@@ -62,7 +62,6 @@ module EbDeployer
62
62
 
63
63
  def package_digest(package)
64
64
  return nil unless package
65
- return package unless File.exists?(package)
66
65
  Digest::MD5.file(package).hexdigest
67
66
  end
68
67
  end
@@ -0,0 +1,40 @@
1
+ module EbDeployer
2
+ module DeploymentStrategy
3
+ class BlueGreen
4
+ def initialize(env)
5
+ @env = env
6
+ end
7
+
8
+ def deploy(version_label, env_settings)
9
+ if !envs.any?(&method(:active_env?))
10
+ env('a', @env.cname_prefix).
11
+ deploy(version_label, env_settings)
12
+ return
13
+ end
14
+
15
+ active_env = envs.detect(&method(:active_env?))
16
+ inactive_env = envs.reject(&method(:active_env?)).first
17
+
18
+ inactive_env.deploy(version_label, env_settings)
19
+ active_env.swap_cname_with(inactive_env)
20
+ end
21
+
22
+ private
23
+ def active_env?(env)
24
+ env.cname_prefix == @env.cname_prefix
25
+ end
26
+
27
+ def envs
28
+ [env('a'), env('b')]
29
+ end
30
+
31
+ def env(suffix, cname_prefix=nil)
32
+ @env.new_eb_env(suffix, cname_prefix || inactive_cname_prefix)
33
+ end
34
+
35
+ def inactive_cname_prefix
36
+ "#{@env.cname_prefix}-inactive"
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,13 @@
1
+ module EbDeployer
2
+ module DeploymentStrategy
3
+ class InplaceUpdate
4
+ def initialize(env)
5
+ @env = env
6
+ end
7
+
8
+ def deploy(version_label, env_settings)
9
+ @env.new_eb_env.deploy(version_label, env_settings)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -1,72 +1,17 @@
1
+ require 'eb_deployer/deployment_strategy/inplace_update'
2
+ require 'eb_deployer/deployment_strategy/blue_green'
3
+
1
4
  module EbDeployer
2
5
  module DeploymentStrategy
3
- class InplaceUpdate
4
- def initialize(app, env_name, eb_driver, env_creation_opts)
5
- @app = app
6
- @env_name = env_name
7
- @eb_driver = eb_driver
8
- @env_creation_opts = env_creation_opts
9
- end
10
-
11
- def deploy(version_label, env_settings)
12
- Environment.new(@app, @env_name, @eb_driver, @env_creation_opts).
13
- deploy(version_label, env_settings)
14
- end
15
- end
16
-
17
- class BlueGreen
18
- def initialize(app, env_name, eb_driver, env_creation_opts)
19
- @app = app
20
- @env_name = env_name
21
- @eb_driver = eb_driver
22
- @env_creation_opts = env_creation_opts
23
- @major_cname_prefix = env_creation_opts[:cname_prefix]
24
- end
25
-
26
- def deploy(version_label, env_settings)
27
- if !envs.any?(&method(:active_env?))
28
- env('a', @major_cname_prefix).
29
- deploy(version_label, env_settings)
30
- return
31
- end
32
-
33
- active_env = envs.detect(&method(:active_env?))
34
- inactive_env = envs.reject(&method(:active_env?)).first
35
-
36
- inactive_env.deploy(version_label, env_settings)
37
- active_env.swap_cname_with(inactive_env)
38
- end
39
-
40
- private
41
- def active_env?(env)
42
- env.cname_prefix == @major_cname_prefix
43
- end
44
-
45
- def envs
46
- [env('a'), env('b')]
47
- end
48
-
49
- def env(suffix, cname_prefix=nil)
50
- Environment.new(@app, @env_name + '-' + suffix,
51
- @eb_driver,
52
- @env_creation_opts.merge({:cname_prefix => cname_prefix || inactive_cname_prefix}))
53
- end
54
-
55
- def inactive_cname_prefix
56
- "#{@app}-#{@env_name}-inactive"
57
- end
58
- end
59
-
60
- def self.create(strategy_name, app, env_name, eb_driver, env_creation_opts={})
6
+ def self.create(env, strategy_name)
61
7
  case strategy_name.to_s
62
8
  when 'inplace_update', 'inplace-update'
63
- InplaceUpdate.new(app, env_name, eb_driver, env_creation_opts)
9
+ InplaceUpdate.new(env)
64
10
  when 'blue_green', 'blue-green'
65
- BlueGreen.new(app, env_name, eb_driver, env_creation_opts)
11
+ BlueGreen.new(env)
66
12
  else
67
13
  raise 'strategy_name: ' + strategy_name.to_s + ' not supported'
68
14
  end
69
-
70
15
  end
71
16
  end
72
17
  end
@@ -0,0 +1,111 @@
1
+ module EbDeployer
2
+ class EbEnvironment
3
+ attr_reader :app, :name
4
+ attr_writer :event_poller
5
+
6
+ def self.unique_ebenv_name(app_name, env_name)
7
+ raise "Environment name #{env_name} is too long, it must be under 15 chars" if env_name.size > 15
8
+ digest = Digest::SHA1.hexdigest(app_name + '-' + env_name)[0..6]
9
+ "#{env_name}-#{digest}"
10
+ end
11
+
12
+ def initialize(app, env_name, eb_driver, creation_opts={})
13
+ @app = app
14
+ @name = self.class.unique_ebenv_name(app, env_name)
15
+ @bs = eb_driver
16
+ @creation_opts = creation_opts
17
+ end
18
+
19
+ def deploy(version_label, settings={})
20
+ terminate if @creation_opts[:phoenix_mode]
21
+ create_or_update_env(version_label, settings)
22
+ smoke_test
23
+ wait_for_env_become_healthy
24
+ end
25
+
26
+ def cname_prefix
27
+ @bs.environment_cname_prefix(@app, @name)
28
+ end
29
+
30
+ def ==(another)
31
+ self.app == another.app && self.name == another.name
32
+ end
33
+
34
+ def swap_cname_with(another)
35
+ log("Swap CNAME with env #{another.name}")
36
+ @bs.environment_swap_cname(self.app, self.name, another.name)
37
+ end
38
+
39
+ def log(msg)
40
+ puts "[#{Time.now.utc}][environment:#{@name}] #{msg}"
41
+ end
42
+
43
+ def terminate
44
+ if @bs.environment_exists?(@app, @name)
45
+ with_polling_events(/terminateEnvironment completed successfully/i) do
46
+ @bs.delete_environment(@app, @name)
47
+ end
48
+ end
49
+ end
50
+
51
+
52
+ private
53
+
54
+ def create_or_update_env(version_label, settings)
55
+ if @bs.environment_exists?(@app, @name)
56
+ with_polling_events(/Environment update completed successfully/i) do
57
+ @bs.update_environment(@app, @name, version_label, @creation_opts[:tier], settings)
58
+ end
59
+ else
60
+ with_polling_events(/Successfully launched environment/i) do
61
+ @bs.create_environment(@app, @name, @creation_opts[:solution_stack], @creation_opts[:cname_prefix], version_label, @creation_opts[:tier], settings)
62
+ end
63
+ end
64
+ end
65
+
66
+ def smoke_test
67
+ host_name = @bs.environment_cname(@app, @name)
68
+ SmokeTest.new(@creation_opts[:smoke_test]).run(host_name, self)
69
+ end
70
+
71
+ def with_polling_events(terminate_pattern, &block)
72
+ event_start_time = Time.now
73
+ yield
74
+ event_poller.poll(event_start_time) do |event|
75
+ if event[:message] =~ /Failed to deploy application/
76
+ raise event[:message]
77
+ end
78
+
79
+ if event[:message] =~ /Command failed on instance/
80
+ raise "Elasticbeanstalk instance provision failed (maybe a problem with your .ebextension files). The original message: #{event[:message]}"
81
+ end
82
+
83
+ log_event(event)
84
+ break if event[:message] =~ terminate_pattern
85
+ end
86
+ end
87
+
88
+ def wait_for_env_become_healthy
89
+ Timeout.timeout(600) do
90
+ current_health_status = @bs.environment_health_state(@app, @name)
91
+
92
+ while current_health_status != 'Green'
93
+ log("health status: #{current_health_status}")
94
+ sleep 15
95
+ current_health_status = @bs.environment_health_state(@app, @name)
96
+ end
97
+
98
+ log("health status: #{current_health_status}")
99
+ end
100
+ end
101
+
102
+ def event_poller
103
+ @event_poller || EventPoller.new(@app, @name, @bs)
104
+ end
105
+
106
+
107
+ def log_event(event)
108
+ puts "[#{event[:event_date]}][environment:#{@name}] #{event[:message]}"
109
+ end
110
+ end
111
+ end
@@ -1,111 +1,45 @@
1
1
  module EbDeployer
2
2
  class Environment
3
- attr_reader :app, :name
4
- attr_writer :event_poller
5
3
 
6
- def self.unique_ebenv_name(app_name, env_name)
7
- raise "Environment name #{env_name} is too long, it must be under 15 chars" if env_name.size > 15
8
- digest = Digest::SHA1.hexdigest(app_name + '-' + env_name)[0..6]
9
- "#{env_name}-#{digest}"
10
- end
11
-
12
- def initialize(app, env_name, eb_driver, creation_opts={})
4
+ def initialize(app, name, resource_stacks, settings, creation_opts, bs_driver)
13
5
  @app = app
14
- @name = self.class.unique_ebenv_name(app, env_name)
15
- @bs = eb_driver
6
+ @name = name
7
+ @resource_stacks = resource_stacks
8
+ @settings = settings
16
9
  @creation_opts = creation_opts
17
- end
18
-
19
- def deploy(version_label, settings={})
20
- terminate if @creation_opts[:phoenix_mode]
21
- create_or_update_env(version_label, settings)
22
- smoke_test
23
- wait_for_env_become_healthy
10
+ @bs_driver = bs_driver
24
11
  end
25
12
 
26
13
  def cname_prefix
27
- @bs.environment_cname_prefix(@app, @name)
28
- end
29
-
30
- def ==(another)
31
- self.app == another.app && self.name == another.name
32
- end
33
-
34
- def swap_cname_with(another)
35
- log("Swap CNAME with env #{another.name}")
36
- @bs.environment_swap_cname(self.app, self.name, another.name)
14
+ @creation_opts[:cname_prefix] || default_cname_prefix
37
15
  end
38
16
 
39
- def log(msg)
40
- puts "[#{Time.now.utc}][environment:#{@name}] #{msg}"
17
+ def deploy(version_label, strategy_name)
18
+ strategy = create_strategy(strategy_name)
19
+ strategy.deploy(version_label,
20
+ @settings + @resource_stacks.provision(resource_stack_name))
41
21
  end
42
22
 
43
- def terminate
44
- if @bs.environment_exists?(@app, @name)
45
- with_polling_events(/terminateEnvironment completed successfully/i) do
46
- @bs.delete_environment(@app, @name)
47
- end
48
- end
23
+ def new_eb_env(suffix=nil, cname_prefix_overriding=nil)
24
+ EbEnvironment.new(@app.name,
25
+ [@name, suffix].compact.join('-'),
26
+ @bs_driver,
27
+ @creation_opts.merge(:cname_prefix => cname_prefix_overriding || cname_prefix))
49
28
  end
50
29
 
51
-
52
30
  private
53
31
 
54
- def create_or_update_env(version_label, settings)
55
- if @bs.environment_exists?(@app, @name)
56
- with_polling_events(/Environment update completed successfully/i) do
57
- @bs.update_environment(@app, @name, version_label, @creation_opts[:tier], settings)
58
- end
59
- else
60
- with_polling_events(/Successfully launched environment/i) do
61
- @bs.create_environment(@app, @name, @creation_opts[:solution_stack], @creation_opts[:cname_prefix], version_label, @creation_opts[:tier], settings)
62
- end
63
- end
32
+ def default_cname_prefix
33
+ [@app.name, @name].join('-')
64
34
  end
65
35
 
66
- def smoke_test
67
- host_name = @bs.environment_cname(@app, @name)
68
- SmokeTest.new(@creation_opts[:smoke_test]).run(host_name, self)
69
- end
70
36
 
71
- def with_polling_events(terminate_pattern, &block)
72
- event_start_time = Time.now
73
- yield
74
- event_poller.poll(event_start_time) do |event|
75
- if event[:message] =~ /Failed to deploy application/
76
- raise event[:message]
77
- end
78
-
79
- if event[:message] =~ /Command failed on instance/
80
- raise "Elasticbeanstalk instance provision failed (maybe a problem with your .ebextension files). The original message: #{event[:message]}"
81
- end
82
-
83
- log_event(event)
84
- break if event[:message] =~ terminate_pattern
85
- end
37
+ def create_strategy(strategy_name)
38
+ DeploymentStrategy.create(self, strategy_name)
86
39
  end
87
40
 
88
- def wait_for_env_become_healthy
89
- Timeout.timeout(600) do
90
- current_health_status = @bs.environment_health_state(@app, @name)
91
-
92
- while current_health_status != 'Green'
93
- log("health status: #{current_health_status}")
94
- sleep 15
95
- current_health_status = @bs.environment_health_state(@app, @name)
96
- end
97
-
98
- log("health status: #{current_health_status}")
99
- end
100
- end
101
-
102
- def event_poller
103
- @event_poller || EventPoller.new(@app, @name, @bs)
104
- end
105
-
106
-
107
- def log_event(event)
108
- puts "[#{event[:event_date]}][environment:#{@name}] #{event[:message]}"
41
+ def resource_stack_name
42
+ "#{@app.name}-#{@name}"
109
43
  end
110
44
  end
111
45
  end
@@ -0,0 +1,19 @@
1
+ module EbDeployer
2
+ class ResourceStacks
3
+ def initialize(resources, cf_driver, skip_provision=false)
4
+ @resources = resources
5
+ @cf_driver = cf_driver
6
+ @skip_provision = skip_provision
7
+ end
8
+
9
+ def provision(stack_name)
10
+ provisioner = CloudFormationProvisioner.new(stack_name, @cf_driver)
11
+ if @resources
12
+ provisioner.provision(@resources) unless @skip_provision
13
+ provisioner.transform_outputs(@resources)
14
+ else
15
+ []
16
+ end
17
+ end
18
+ end
19
+ end
@@ -1,3 +1,3 @@
1
1
  module EbDeployer
2
- VERSION = "0.3.6"
2
+ VERSION = "0.3.7"
3
3
  end
data/lib/eb_deployer.rb CHANGED
@@ -1,17 +1,3 @@
1
- require "eb_deployer/version"
2
- require "eb_deployer/deployment_strategy"
3
- require "eb_deployer/beanstalk"
4
- require "eb_deployer/cloud_formation_provisioner"
5
- require 'eb_deployer/application'
6
- require "eb_deployer/environment"
7
- require "eb_deployer/event_poller"
8
- require "eb_deployer/package"
9
- require 'eb_deployer/s3_driver'
10
- require 'eb_deployer/cloud_formation_driver'
11
- require 'eb_deployer/config_loader'
12
- require 'eb_deployer/default_config'
13
- require 'eb_deployer/smoke_test'
14
- require 'eb_deployer/version_cleaner'
15
1
  require 'digest'
16
2
  require 'set'
17
3
  require 'time'
@@ -22,6 +8,21 @@ require 'optparse'
22
8
  require 'erb'
23
9
  require 'fileutils'
24
10
 
11
+ require 'eb_deployer/version'
12
+ require 'eb_deployer/aws_driver'
13
+ require 'eb_deployer/deployment_strategy'
14
+ require 'eb_deployer/cloud_formation_provisioner'
15
+ require 'eb_deployer/application'
16
+ require 'eb_deployer/resource_stacks'
17
+ require 'eb_deployer/eb_environment'
18
+ require 'eb_deployer/environment'
19
+ require 'eb_deployer/event_poller'
20
+ require 'eb_deployer/package'
21
+ require 'eb_deployer/config_loader'
22
+ require 'eb_deployer/default_config'
23
+ require 'eb_deployer/smoke_test'
24
+ require 'eb_deployer/version_cleaner'
25
+
25
26
  module EbDeployer
26
27
 
27
28
  TIERS = [
@@ -48,7 +49,7 @@ module EbDeployer
48
49
  end
49
50
  app = opts[:application]
50
51
  env_name = opts[:environment]
51
- cf = opts[:cf_driver] || CloudFormationDriver.new
52
+ cf = opts[:cf_driver] || AWSDriver::CloudFormationDriver.new
52
53
  provisioner = CloudFormationProvisioner.new("#{app}-#{env_name}", cf)
53
54
  provisioner.output(key)
54
55
  end
@@ -177,9 +178,9 @@ module EbDeployer
177
178
  AWS.config(:region => region)
178
179
  end
179
180
 
180
- bs = opts[:bs_driver] || Beanstalk.new
181
- s3 = opts[:s3_driver] || S3Driver.new
182
- cf = opts[:cf_driver] || CloudFormationDriver.new
181
+ bs = opts[:bs_driver] || AWSDriver::Beanstalk.new
182
+ s3 = opts[:s3_driver] || AWSDriver::S3Driver.new
183
+ cf = opts[:cf_driver] || AWSDriver::CloudFormationDriver.new
183
184
  stack_name = opts[:solution_stack_name] || "64bit Amazon Linux 2013.09 running Tomcat 7 Java 7"
184
185
  app = opts[:application]
185
186
  env_name = opts[:environment]
@@ -188,7 +189,7 @@ module EbDeployer
188
189
  cname = opts[:cname]
189
190
  env_settings = opts[:option_settings] || opts[:settings] || []
190
191
  strategy_name = opts[:strategy] || :blue_green
191
- cname_prefix = opts[:cname_prefix] || [app, env_name].join('-')
192
+ cname_prefix = opts[:cname_prefix]
192
193
  smoke_test = opts[:smoke_test] || Proc.new {}
193
194
  phoenix_mode = opts[:phoenix_mode]
194
195
  bucket = opts[:package_bucket] || app
@@ -196,30 +197,24 @@ module EbDeployer
196
197
  keep_latest = opts[:keep_latest].to_i || 0
197
198
  app_tier = self.environment_tier(opts[:tier] || 'WebServer')
198
199
 
200
+ resource_stacks = ResourceStacks.new(opts[:resources], cf, skip_resource)
199
201
  application = Application.new(app, bs, s3, bucket)
200
-
201
- cf = CloudFormationProvisioner.new("#{app}-#{env_name}", cf)
202
-
203
- creation_opts = {
204
- :solution_stack => stack_name,
205
- :cname_prefix => cname_prefix,
206
- :smoke_test => smoke_test,
207
- :phoenix_mode => phoenix_mode,
208
- :tier => app_tier
209
- }
210
-
211
- strategy = DeploymentStrategy.create(strategy_name, app, env_name, bs, creation_opts)
212
-
213
- cleaner = VersionCleaner.new(application, keep_latest)
214
-
215
- if resources = opts[:resources]
216
- cf.provision(resources) unless skip_resource
217
- env_settings += cf.transform_outputs(resources)
218
- end
202
+ environment = Environment.new(application,
203
+ env_name,
204
+ resource_stacks,
205
+ env_settings,
206
+ {
207
+ :solution_stack => stack_name,
208
+ :cname_prefix => cname_prefix,
209
+ :smoke_test => smoke_test,
210
+ :phoenix_mode => phoenix_mode,
211
+ :tier => app_tier
212
+ },
213
+ bs)
219
214
 
220
215
  application.create_version(version_label, opts[:package])
221
- strategy.deploy(version_label, env_settings)
222
- cleaner.clean(version_prefix)
216
+ environment.deploy(version_label, strategy_name)
217
+ application.clean_versions(version_prefix, keep_latest)
223
218
  end
224
219
 
225
220
  def self.destroy(opts)
@@ -313,7 +308,6 @@ module EbDeployer
313
308
  puts "YAML package file format:"
314
309
  puts "s3_bucket: <bucket_name>"
315
310
  puts "s3_key: <object_path>"
316
- exit(0)
317
311
  end
318
312
  end
319
313
  end
@@ -25,19 +25,6 @@ YAML
25
25
  assert_equal(nil, config[:common])
26
26
  end
27
27
 
28
- def test_use_package_as_version_label_when_using_s3_obj_as_package
29
- config = @loader.load(generate_input(<<-YAML, :package => 'bucket:obj'))
30
- application: myapp
31
- common:
32
- strategy: inplace-update
33
- environments:
34
- dev:
35
- YAML
36
- assert_equal('myapp', config[:application])
37
- assert_equal('bucket:obj', config[:package])
38
- assert_equal('bucket:obj', config[:version_label])
39
- end
40
-
41
28
  def test_common_settings_get_merge_into_the_config
42
29
  config = @loader.load(generate_input(<<-YAML))
43
30
  application: myapp
data/test/deploy_test.rb CHANGED
@@ -432,6 +432,17 @@ class DeployTest < MiniTest::Unit::TestCase
432
432
  assert_equal({'s3_bucket' => 'test-bucket', 's3_key' => 'test-mingle.war'}, last_version[:source_bundle])
433
433
  end
434
434
 
435
+ =begin
436
+ def test_deploy_with_components
437
+ deploy(:application => 'simple',
438
+ :environment => 'production',
439
+ :components => [{ :name => 'web' }])
440
+
441
+ assert !@eb_driver.environment_exists?('simple', eb_envname('simple', 'production'))
442
+
443
+ end
444
+ =end
445
+
435
446
  private
436
447
 
437
448
  def temp_file(content)
@@ -1,6 +1,6 @@
1
1
  require 'test_helper'
2
2
 
3
- class EnvironmentTest < MiniTest::Unit::TestCase
3
+ class EbEnvironmentTest < MiniTest::Unit::TestCase
4
4
  class PollerStub
5
5
  class Deadloop < StandardError; end
6
6
 
@@ -25,13 +25,13 @@ class EnvironmentTest < MiniTest::Unit::TestCase
25
25
  end
26
26
 
27
27
  def test_deploy_should_create_corresponding_eb_env
28
- env = EbDeployer::Environment.new("myapp", "production", @eb_driver)
28
+ env = EbDeployer::EbEnvironment.new("myapp", "production", @eb_driver)
29
29
  env.deploy("version1")
30
30
  assert @eb_driver.environment_exists?('myapp', eb_envname('myapp', 'production'))
31
31
  end
32
32
 
33
33
  def test_deploy_again_should_update_environment
34
- env = EbDeployer::Environment.new("myapp", "production", @eb_driver)
34
+ env = EbDeployer::EbEnvironment.new("myapp", "production", @eb_driver)
35
35
  env.deploy("version1")
36
36
  env.deploy("version2")
37
37
  assert @eb_driver.environment_exists?('myapp', eb_envname('myapp', 'production'))
@@ -39,14 +39,14 @@ class EnvironmentTest < MiniTest::Unit::TestCase
39
39
  end
40
40
 
41
41
  def test_option_setttings_get_set_on_eb_env
42
- env = EbDeployer::Environment.new("myapp", "production", @eb_driver)
42
+ env = EbDeployer::EbEnvironment.new("myapp", "production", @eb_driver)
43
43
  env.deploy("version1", {s1: 'v1'})
44
44
  assert_equal({s1: 'v1' }, @eb_driver.environment_settings('myapp', eb_envname('myapp', 'production')))
45
45
  end
46
46
 
47
47
  def test_should_run_smoke_test_after_deploy
48
48
  smoked_host = nil
49
- env = EbDeployer::Environment.new("myapp", "production", @eb_driver, :smoke_test => Proc.new { |host| smoked_host = host })
49
+ env = EbDeployer::EbEnvironment.new("myapp", "production", @eb_driver, :smoke_test => Proc.new { |host| smoked_host = host })
50
50
  env.deploy("version1")
51
51
 
52
52
  assert !smoked_host.nil?
@@ -54,13 +54,13 @@ class EnvironmentTest < MiniTest::Unit::TestCase
54
54
  end
55
55
 
56
56
  def test_should_raise_runtime_error_when_deploy_failed
57
- env = EbDeployer::Environment.new("myapp", "production", @eb_driver)
57
+ env = EbDeployer::EbEnvironment.new("myapp", "production", @eb_driver)
58
58
  env.event_poller = PollerStub.new(["start deploying", "Failed to deploy application"])
59
59
  assert_raises(RuntimeError) { env.deploy("version 1") }
60
60
  end
61
61
 
62
62
  def test_should_raise_runtime_error_when_eb_extension_execution_failed
63
- env = EbDeployer::Environment.new("myapp", "production", @eb_driver)
63
+ env = EbDeployer::EbEnvironment.new("myapp", "production", @eb_driver)
64
64
 
65
65
  env.event_poller = PollerStub.new(["start deploying",
66
66
  "create environment",
@@ -72,7 +72,7 @@ class EnvironmentTest < MiniTest::Unit::TestCase
72
72
 
73
73
 
74
74
  def test_terminate_should_delete_environment
75
- env = EbDeployer::Environment.new("myapp", "production", @eb_driver)
75
+ env = EbDeployer::EbEnvironment.new("myapp", "production", @eb_driver)
76
76
  env.deploy("version1")
77
77
  env.terminate
78
78
  assert !@eb_driver.environment_exists?('myapp', eb_envname('myapp', 'production'))
data/test/test_helper.rb CHANGED
@@ -15,7 +15,7 @@ class MiniTest::Unit::TestCase
15
15
  end
16
16
 
17
17
  def eb_envname(app_name, env_name)
18
- EbDeployer::Environment.unique_ebenv_name(app_name, env_name)
18
+ EbDeployer::EbEnvironment.unique_ebenv_name(app_name, env_name)
19
19
  end
20
20
 
21
21
  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.3.6
4
+ version: 0.3.7
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2014-03-10 00:00:00.000000000 Z
13
+ date: 2014-03-26 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: aws-sdk
@@ -40,6 +40,7 @@ files:
40
40
  - .gitignore
41
41
  - .ruby-gemset
42
42
  - .ruby-version
43
+ - .travis.yml
43
44
  - Gemfile
44
45
  - LICENSE
45
46
  - README.md
@@ -48,17 +49,22 @@ files:
48
49
  - eb_deployer.gemspec
49
50
  - lib/eb_deployer.rb
50
51
  - lib/eb_deployer/application.rb
51
- - lib/eb_deployer/beanstalk.rb
52
- - lib/eb_deployer/cloud_formation_driver.rb
52
+ - lib/eb_deployer/aws_driver.rb
53
+ - lib/eb_deployer/aws_driver/beanstalk.rb
54
+ - lib/eb_deployer/aws_driver/cloud_formation_driver.rb
55
+ - lib/eb_deployer/aws_driver/s3_driver.rb
53
56
  - lib/eb_deployer/cloud_formation_provisioner.rb
54
57
  - lib/eb_deployer/config_loader.rb
55
58
  - lib/eb_deployer/default_config.rb
56
59
  - lib/eb_deployer/default_config.yml
57
60
  - lib/eb_deployer/deployment_strategy.rb
61
+ - lib/eb_deployer/deployment_strategy/blue_green.rb
62
+ - lib/eb_deployer/deployment_strategy/inplace_update.rb
63
+ - lib/eb_deployer/eb_environment.rb
58
64
  - lib/eb_deployer/environment.rb
59
65
  - lib/eb_deployer/event_poller.rb
60
66
  - lib/eb_deployer/package.rb
61
- - lib/eb_deployer/s3_driver.rb
67
+ - lib/eb_deployer/resource_stacks.rb
62
68
  - lib/eb_deployer/smoke_test.rb
63
69
  - lib/eb_deployer/version.rb
64
70
  - lib/eb_deployer/version_cleaner.rb
@@ -66,7 +72,7 @@ files:
66
72
  - test/cloud_formation_provisioner_test.rb
67
73
  - test/config_loader_test.rb
68
74
  - test/deploy_test.rb
69
- - test/environment_test.rb
75
+ - test/eb_environment_test.rb
70
76
  - test/smoke_test_test.rb
71
77
  - test/test_helper.rb
72
78
  homepage: https://github.com/ThoughtWorksStudios/eb_deployer
@@ -82,15 +88,21 @@ required_ruby_version: !ruby/object:Gem::Requirement
82
88
  - - ! '>='
83
89
  - !ruby/object:Gem::Version
84
90
  version: '0'
91
+ segments:
92
+ - 0
93
+ hash: 1041206842017006108
85
94
  required_rubygems_version: !ruby/object:Gem::Requirement
86
95
  none: false
87
96
  requirements:
88
97
  - - ! '>='
89
98
  - !ruby/object:Gem::Version
90
99
  version: '0'
100
+ segments:
101
+ - 0
102
+ hash: 1041206842017006108
91
103
  requirements: []
92
104
  rubyforge_project:
93
- rubygems_version: 1.8.23
105
+ rubygems_version: 1.8.29
94
106
  signing_key:
95
107
  specification_version: 3
96
108
  summary: Low friction deployments should be a breeze. Elastic Beanstalk provides a
@@ -101,7 +113,7 @@ test_files:
101
113
  - test/cloud_formation_provisioner_test.rb
102
114
  - test/config_loader_test.rb
103
115
  - test/deploy_test.rb
104
- - test/environment_test.rb
116
+ - test/eb_environment_test.rb
105
117
  - test/smoke_test_test.rb
106
118
  - test/test_helper.rb
107
119
  has_rdoc:
@@ -1,126 +0,0 @@
1
- module EbDeployer
2
- class Beanstalk
3
- attr_reader :client
4
-
5
- def initialize(client=AWS::ElasticBeanstalk.new.client)
6
- @client = client
7
- end
8
-
9
- def create_application(app)
10
- @client.create_application(:application_name => app)
11
- end
12
-
13
- def delete_application(app)
14
- @client.delete_application(:application_name => app)
15
- end
16
-
17
- def application_exists?(app)
18
- @client.describe_applications(:application_names => [app])[:applications].any?
19
- end
20
-
21
- def update_environment(app_name, env_name, version, tier, settings)
22
- env_id = convert_env_name_to_id(app_name, [env_name]).first
23
- request = {
24
- :environment_id => env_id,
25
- :version_label => version,
26
- :option_settings => settings,
27
- :tier => tier
28
- }
29
-
30
- @client.update_environment(request)
31
- end
32
-
33
- def environment_exists?(app_name, env_name)
34
- alive_envs(app_name, [env_name]).any?
35
- end
36
-
37
- def environment_names_for_application(app_name)
38
- alive_envs(app_name).collect { |env| env[:environment_name] }
39
- end
40
-
41
- def create_environment(app_name, env_name, stack_name, cname_prefix, version, tier, settings)
42
- request = {
43
- :application_name => app_name,
44
- :environment_name => env_name,
45
- :solution_stack_name => stack_name,
46
- :version_label => version,
47
- :option_settings => settings,
48
- :tier => tier,
49
- :cname_prefix => cname_prefix
50
- }
51
-
52
- @client.create_environment(request)
53
- end
54
-
55
- def delete_environment(app_name, env_name)
56
- @client.terminate_environment(:environment_name => env_name)
57
- end
58
-
59
- def delete_application_version(app_name, version, delete_source_bundle)
60
- request = {
61
- :application_name => app_name,
62
- :version_label => version,
63
- :delete_source_bundle => delete_source_bundle
64
- }
65
- @client.delete_application_version(request)
66
- end
67
-
68
- def create_application_version(app_name, version_label, source_bundle)
69
- @client.create_application_version(:application_name => app_name,
70
- :source_bundle => source_bundle,
71
- :version_label => version_label)
72
- end
73
-
74
- def application_version_labels(app_name)
75
- application_versions(app_name).map { |apv| apv[:version_label] }
76
- end
77
-
78
- def application_versions(app_name)
79
- request = { :application_name => app_name }
80
- @client.describe_application_versions(request)[:application_versions]
81
- end
82
-
83
- def fetch_events(app_name, env_name, params, &block)
84
- response = @client.describe_events(params.merge(:application_name => app_name,
85
- :environment_name => env_name))
86
- return [response[:events], response[:next_token]]
87
- end
88
-
89
- def environment_cname_prefix(app_name, env_name)
90
- cname = environment_cname(app_name, env_name)
91
- if cname =~ /^(.+)\.elasticbeanstalk\.com/
92
- $1
93
- end
94
- end
95
-
96
- def environment_cname(app_name, env_name)
97
- env = alive_envs(app_name, [env_name]).first
98
- env && env[:cname]
99
- end
100
-
101
- def environment_health_state(app_name, env_name)
102
- env = alive_envs(app_name, [env_name]).first
103
- env && env[:health]
104
- end
105
-
106
- def environment_swap_cname(app_name, env1, env2)
107
- env1_id, env2_id = convert_env_name_to_id(app_name, [env1, env2])
108
- @client.swap_environment_cnam_es(:source_environment_id => env1_id,
109
- :destination_environment_id => env2_id)
110
- end
111
-
112
- private
113
-
114
- def convert_env_name_to_id(app_name, env_names)
115
- envs = alive_envs(app_name, env_names)
116
- envs.map { |env| env[:environment_id] }
117
- end
118
-
119
- def alive_envs(app_name, env_names=[])
120
- envs = @client.describe_environments(:application_name => app_name, :environment_names => env_names)[:environments]
121
-
122
- envs.select {|e| e[:status] != 'Terminated' }
123
- end
124
-
125
- end
126
- end
@@ -1,48 +0,0 @@
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, template, opts)
10
- end
11
-
12
- def update_stack(name, template, opts)
13
- begin
14
- stack(name).update(opts.merge(:template => 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
- output = stack(name).outputs.find { |o| o.key == key }
30
- output && output.value
31
- end
32
-
33
- private
34
-
35
- def cloud_formation
36
- AWS::CloudFormation.new
37
- end
38
-
39
- def stack(name)
40
- cloud_formation.stacks[name]
41
- end
42
-
43
- def log(msg)
44
- puts "[#{Time.now.utc}][cloud_formation_driver] #{msg}"
45
- end
46
- end
47
-
48
- end
@@ -1,33 +0,0 @@
1
- module EbDeployer
2
- class S3Driver
3
- def create_bucket(bucket_name)
4
- buckets.create(bucket_name)
5
- end
6
-
7
- def bucket_exists?(bucket_name)
8
- buckets[bucket_name].exists?
9
- end
10
-
11
- def object_length(bucket_name, obj_name)
12
- obj(bucket_name, obj_name).content_length rescue nil
13
- end
14
-
15
- def upload_file(bucket_name, obj_name, file)
16
- o = obj(bucket_name, obj_name)
17
- File.open(file) { |f| o.write(f) }
18
- end
19
-
20
- private
21
- def s3
22
- AWS::S3.new
23
- end
24
-
25
- def obj(bucket_name, obj_name)
26
- buckets[bucket_name].objects[obj_name]
27
- end
28
-
29
- def buckets
30
- s3.buckets
31
- end
32
- end
33
- end