eb_deployer 0.3.6 → 0.3.7

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