man_eb_deployer 0.8.0

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.
Files changed (68) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/release.yml +31 -0
  3. data/.github/workflows/test.yml +16 -0
  4. data/.gitignore +12 -0
  5. data/.ruby-gemset +1 -0
  6. data/.ruby-version +1 -0
  7. data/CHANGELOG.md +143 -0
  8. data/Gemfile +10 -0
  9. data/LICENSE +22 -0
  10. data/README.md +138 -0
  11. data/Rakefile +12 -0
  12. data/TODOS.md +11 -0
  13. data/bin/eb_deploy +13 -0
  14. data/eb_deployer.gemspec +22 -0
  15. data/lib/eb_deployer/application.rb +96 -0
  16. data/lib/eb_deployer/aws_driver/beanstalk.rb +158 -0
  17. data/lib/eb_deployer/aws_driver/cloud_formation_driver.rb +53 -0
  18. data/lib/eb_deployer/aws_driver/s3_driver.rb +35 -0
  19. data/lib/eb_deployer/aws_driver.rb +8 -0
  20. data/lib/eb_deployer/cf_event_source.rb +26 -0
  21. data/lib/eb_deployer/cloud_formation_provisioner.rb +120 -0
  22. data/lib/eb_deployer/component.rb +45 -0
  23. data/lib/eb_deployer/config_loader.rb +64 -0
  24. data/lib/eb_deployer/default_component.rb +32 -0
  25. data/lib/eb_deployer/default_config.rb +20 -0
  26. data/lib/eb_deployer/default_config.yml +159 -0
  27. data/lib/eb_deployer/deployment_strategy/blue_green.rb +79 -0
  28. data/lib/eb_deployer/deployment_strategy/blue_only.rb +45 -0
  29. data/lib/eb_deployer/deployment_strategy/inplace_update.rb +16 -0
  30. data/lib/eb_deployer/deployment_strategy.rb +20 -0
  31. data/lib/eb_deployer/eb_environment.rb +204 -0
  32. data/lib/eb_deployer/eb_event_source.rb +35 -0
  33. data/lib/eb_deployer/environment.rb +60 -0
  34. data/lib/eb_deployer/event_poller.rb +51 -0
  35. data/lib/eb_deployer/package.rb +39 -0
  36. data/lib/eb_deployer/resource_stacks.rb +20 -0
  37. data/lib/eb_deployer/smoke_test.rb +23 -0
  38. data/lib/eb_deployer/tasks.rb +45 -0
  39. data/lib/eb_deployer/throttling_handling.rb +17 -0
  40. data/lib/eb_deployer/utils.rb +33 -0
  41. data/lib/eb_deployer/version.rb +3 -0
  42. data/lib/eb_deployer/version_cleaner.rb +30 -0
  43. data/lib/eb_deployer.rb +339 -0
  44. data/lib/generators/eb_deployer/install/install_generator.rb +82 -0
  45. data/lib/generators/eb_deployer/install/templates/eb_deployer.rake +1 -0
  46. data/lib/generators/eb_deployer/install/templates/eb_deployer.yml.erb +181 -0
  47. data/lib/generators/eb_deployer/install/templates/ebextensions/01_postgres_packages.config +5 -0
  48. data/lib/generators/eb_deployer/install/templates/postgres_rds.json +88 -0
  49. data/test/aws_driver_stubs.rb +350 -0
  50. data/test/beanstalk_test.rb +23 -0
  51. data/test/blue_green_deploy_test.rb +114 -0
  52. data/test/blue_only_deploy_test.rb +78 -0
  53. data/test/cf_event_poller_test.rb +32 -0
  54. data/test/cloud_formation_provisioner_test.rb +47 -0
  55. data/test/config_loader_test.rb +205 -0
  56. data/test/deploy_test.rb +42 -0
  57. data/test/eb_environment_test.rb +120 -0
  58. data/test/eb_event_poller_test.rb +32 -0
  59. data/test/inplace_update_deploy_test.rb +110 -0
  60. data/test/multi_components_deploy_test.rb +164 -0
  61. data/test/rails_generators_test.rb +67 -0
  62. data/test/resources_deploy_test.rb +191 -0
  63. data/test/smoke_test_test.rb +23 -0
  64. data/test/template_deploy_test.rb +13 -0
  65. data/test/test_helper.rb +68 -0
  66. data/test/tier_setting_deploy_test.rb +24 -0
  67. data/test/versions_deploy_test.rb +120 -0
  68. metadata +176 -0
@@ -0,0 +1,159 @@
1
+ # application name
2
+ application: <%= app_name %>
3
+
4
+ # common settings for all environments
5
+ common:
6
+ # Solution stack for elastic beanstalk, default is 64bit tomcat 7 for JAVA app
7
+ # solution_stack_name: 64bit Amazon Linux 2014.03 v1.0.3 running Tomcat 7 Java 7
8
+
9
+ # Tier name for environments. Current supported values are WebServer and Worker
10
+ # tier: WebServer
11
+
12
+ # AWS region to deploy. Default to us-east-1
13
+ # region: us-west-1
14
+
15
+ # There are three deployment strategies: 'blue-green', 'blue-only', or 'inplace-update'.
16
+ # Blue green deployments keep two elastic beanstalk environments and always deploy to
17
+ # inactive one, to achieve zero downtime.
18
+ # Blue only deployments do everything that the blue green deployments do except for the final
19
+ # inactive to active CNAME swap leaving the newly deployed application on the inactive
20
+ # "blue" instance.
21
+ # Inplace-update strategy will only keep one environment, and update the version inplace on
22
+ # deploy. Inplace-update will save resources but will suffer from downtime.
23
+ # (All old environments need be destroyed when you switching between strategies.)
24
+ # Default strategy is 'blue-green'.
25
+ # strategy: blue-green
26
+
27
+ # Name of s3 bucket where uploaded application packages will be stored.
28
+ # Note that the string ".packages" will be added as a suffix to your bucket.
29
+ # So, if "thoughtworks.simple" is passed as the bucket name, the actual s3 bucket
30
+ # name will be thoughtworks.simple.packages. Default to application name.
31
+ # package_bucket: my-s3-bucket
32
+
33
+ # If phoenix mode is turned on, it will terminate the old elastic
34
+ # beanstalk environment and recreate on deploy. For blue-green
35
+ # deployment it terminates the inactive environment first then
36
+ # recreate it. This is useful to avoid configuration drift and
37
+ # accumulating state on the ec2 instances. Default is off but we recommend
38
+ # it to be turned on for production environment.
39
+ # phoenix_mode: off
40
+
41
+ # The tags you would like to be associated with your resources.
42
+ # These tags will only be used when you first launch an environment. If you are using
43
+ # phoenix_mode set as true each time you deploy you will get a new environment and therefore
44
+ # any changes to your tags. If phoenix_mode is false then it will only use your tags on the
45
+ # initial deploy.
46
+ # tags:
47
+ # my_tag_key: my_tag_value
48
+
49
+ # Specifies the maximum number of versions to keep. Older versions are removed
50
+ # and deleted from the S3 source bucket as well. If specified as zero or not
51
+ # specified, all versions will be kept. If a version_prefix is given, only removes
52
+ # version starting with the prefix.
53
+ # keep_latest: 200
54
+
55
+ # Specifies a prefix to prepend to the version label.
56
+ # This can be useful if you want to use different binaries for different
57
+ # environments.
58
+ # version_prefix:
59
+
60
+ # Generating version label for package to be deployed. A readable version label will
61
+ # provide better traceablity of your deployment process.
62
+ # By default setting is:
63
+ # version_label: <%%= package_digest %>
64
+ # which means using MD5 digest of the package file. If you deploy using build
65
+ # pipeline tool such as GO, switching to pipline counter is highly suggested to
66
+ # increase the readability. Following example will read pipeline counter from environment
67
+ # variable with a fall back to digest for local deployment:
68
+ # version_label: <%%= ENV['GO_PIPELINE_COUNTER'] || package_digest %>
69
+
70
+
71
+ # Smoke test value should be a piece of ruby code with access to single variable
72
+ # "host_name" -- environment DNS name. Smoke test snippet will be evaluated at
73
+ # the end of the deployment for inplace-update deployment. For blue-green
74
+ # deployment it will run after inactive environment update is completed and before
75
+ # switching over.
76
+ # Defining a smoke test is highly recommended for serious usage. By default we use
77
+ # The simplest one that just be checking server landing page using curl, e.g.
78
+ smoke_test: |
79
+ curl_http_code = "curl -s -o /dev/null -w \"%{http_code}\" http://#{host_name}"
80
+ Timeout.timeout(600) do
81
+ until ['200', '301', '302'].include?(`#{curl_http_code}`.strip)
82
+ sleep 5
83
+ end
84
+ end
85
+
86
+ # List of Elastic Beanstalk health states that will be considered healthy when deploying.
87
+ # You may want to override default 'Green' with that list, if for example you are using
88
+ # Elastic Beanstalk worker tier and you don't want eb_deployer to fail if you have messages
89
+ # in your SQS queue.
90
+ # By default eb_deployer only considers 'Green' as the healthy state.
91
+ # For a list of all Elastic Beanstalk health states refer to:
92
+ # http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/using-features.healthstatus.html#using-features.healthstatus.colors
93
+ # accepted_healthy_states:
94
+ # - Green
95
+ # - Yellow
96
+
97
+ # Elastic Beanstalk settings that will apply to the environments you are
98
+ # deploying.
99
+ # For all available options take a look at
100
+ # http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/command-options.html
101
+ option_settings:
102
+ # Following is an example of set EC2 ssh key name. This allow you ssh into the ec2
103
+ # instance. See http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-key-pairs.html
104
+ # - namespace: aws:autoscaling:launchconfiguration
105
+ # option_name: EC2KeyName
106
+ # value: <my-ec2-key-name>
107
+
108
+ # Following is an example which changes EC2 instance type
109
+ # from t1.micro (default) to m1.small. Instances with t1.micro type sometime
110
+ # are not very responsible, so m1.small is suggested for saving time.
111
+ # But if you care about the marginal cost difference you can comment this out to
112
+ # go back to t1.micro.
113
+ - namespace: aws:autoscaling:launchconfiguration
114
+ option_name: InstanceType
115
+ value: m1.small
116
+
117
+
118
+ # If resources specified, eb_deployer will use the CloudFormation
119
+ # template you provide to create a default CloudFormation stack with
120
+ # name <application_name>-<env-name> for the environment current
121
+ # deploying. And Outputs of the CloudFormation can be mapped to Elastic Beanstalk
122
+ # options settings.
123
+ # keys:
124
+ # template => CloudFormation template file with JSON format
125
+ # policy => CloudFormation policy file with JSON format
126
+ # override_policy => (false) If override_policy is true and a policy file is provided then the policy will temporarily override any existing policy on the resource stack during this update,
127
+ # otherwise the provided policy will replace any existing policy on the resource stack
128
+ # inputs => A Hash, input values for the CloudFormation template
129
+ # outputs => A Hash with key map to your CloudFormation template outputs and value as elastic beanstalk settings namespace and option_name.
130
+ # capabilities => An array. You need set it to ['CAPABILITY_IAM'] if the
131
+ # template include IAM Instance Profile.
132
+ resources:
133
+
134
+ # For example creating a RDS instance for blue green deployment(check jruby-rails4
135
+ # sample project which has a working example):
136
+ # template: config/my_rds.json
137
+ # inputs:
138
+ # DBPassword: <%%= ENV["MYDBPASSWORD"] %>
139
+ # outputs:
140
+ # RDSPassSecurityGroup:
141
+ # namespace: aws:autoscaling:launchconfiguration
142
+ # option_name: SecurityGroups
143
+ # RDSDatabaseConfig:
144
+ # namespace: aws:elasticbeanstalk:application:environment
145
+ # option_name: databaseConfig
146
+
147
+
148
+ # You can define environment here. Each environment can overriden any common settings
149
+ environments:
150
+ dev:
151
+ # example for overriding common settings
152
+ # strategy: inplace-update
153
+ production:
154
+ option_settings:
155
+ # example for overriding common option_settings: providing least redundancy
156
+ # in production environment.
157
+ # - namespace: aws:autoscaling:asg
158
+ # option_name: MinSize
159
+ # value: "2"
@@ -0,0 +1,79 @@
1
+ module EbDeployer
2
+ module DeploymentStrategy
3
+ class BlueGreen
4
+ def initialize(component)
5
+ @component = component
6
+ end
7
+
8
+ def test_compatibility(env_create_options)
9
+ @create_opts = env_create_options
10
+ tier = env_create_options[:tier]
11
+
12
+ if tier && tier.downcase == 'worker'
13
+ raise "Blue green deployment is not supported for Worker tier"
14
+ end
15
+ end
16
+
17
+ def deploy(version_label, env_settings, inactive_settings=[])
18
+
19
+ if !ebenvs.any?(&method(:active_ebenv?))
20
+ ebenv('a', @component.cname_prefix).
21
+ deploy(version_label, env_settings)
22
+ return
23
+ end
24
+
25
+ active_ebenv = ebenvs.detect(&method(:active_ebenv?))
26
+ inactive_ebenv = ebenvs.reject(&method(:active_ebenv?)).first
27
+
28
+ inactive_ebenv.deploy(version_label, env_settings)
29
+ active_ebenv.swap_cname_with(inactive_ebenv)
30
+
31
+ blue_green_terminate_inactive = @create_opts[:blue_green_terminate_inactive]
32
+ blue_green_terminate_inactive_wait = @create_opts[:blue_green_terminate_inactive_wait]
33
+ blue_green_terminate_inactive_sleep = @create_opts[:blue_green_terminate_inactive_sleep]
34
+
35
+ if blue_green_terminate_inactive
36
+ active_ebenv.log("Waiting #{blue_green_terminate_inactive_wait}s before terminating environment...")
37
+
38
+ # Loop until timeout reached or environment becomes Red
39
+ count = 0
40
+ loop do
41
+ break if count >= blue_green_terminate_inactive_wait or inactive_ebenv.health_state != 'Green'
42
+ sleep blue_green_terminate_inactive_sleep
43
+ count += blue_green_terminate_inactive_sleep
44
+ end
45
+
46
+ if inactive_ebenv.health_state == 'Green'
47
+ active_ebenv.log("Active environment healthy, terminating inactive (black) environment")
48
+ active_ebenv.terminate
49
+ else
50
+ active_ebenv.log("Active environment changed state to unhealthy. Existing (black) environment will not be terminated")
51
+ end
52
+
53
+ end
54
+
55
+ unless inactive_settings.empty? || blue_green_terminate_inactive
56
+ active_ebenv.log("applying inactive settings...")
57
+ active_ebenv.apply_settings(inactive_settings)
58
+ end
59
+ end
60
+
61
+ private
62
+ def active_ebenv?(ebenv)
63
+ ebenv.cname_prefix == @component.cname_prefix
64
+ end
65
+
66
+ def ebenvs
67
+ [ebenv('a'), ebenv('b')]
68
+ end
69
+
70
+ def ebenv(suffix, cname_prefix=nil)
71
+ @component.new_eb_env(suffix, cname_prefix || inactive_cname_prefix)
72
+ end
73
+
74
+ def inactive_cname_prefix
75
+ "#{@component.cname_prefix}-inactive"
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,45 @@
1
+ module EbDeployer
2
+ module DeploymentStrategy
3
+ class BlueOnly
4
+ def initialize(component)
5
+ @component = component
6
+ end
7
+
8
+ def test_compatibility(env_create_options)
9
+ tier = env_create_options[:tier]
10
+ if tier && tier.downcase == 'worker'
11
+ raise "Blue only deployment is not supported for Worker tier"
12
+ end
13
+ end
14
+
15
+ def deploy(version_label, env_settings, inactive_settings=[])
16
+ if !ebenvs.any?(&method(:active_ebenv?))
17
+ ebenv('a', @component.cname_prefix).
18
+ deploy(version_label, env_settings)
19
+ return
20
+ end
21
+
22
+ inactive_ebenv = ebenvs.reject(&method(:active_ebenv?)).first
23
+
24
+ inactive_ebenv.deploy(version_label, env_settings)
25
+ end
26
+
27
+ private
28
+ def active_ebenv?(ebenv)
29
+ ebenv.cname_prefix == @component.cname_prefix
30
+ end
31
+
32
+ def ebenvs
33
+ [ebenv('a'), ebenv('b')]
34
+ end
35
+
36
+ def ebenv(suffix, cname_prefix=nil)
37
+ @component.new_eb_env(suffix, cname_prefix || inactive_cname_prefix)
38
+ end
39
+
40
+ def inactive_cname_prefix
41
+ "#{@component.cname_prefix}-inactive"
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,16 @@
1
+ module EbDeployer
2
+ module DeploymentStrategy
3
+ class InplaceUpdate
4
+ def initialize(component)
5
+ @component = component
6
+ end
7
+
8
+ def test_compatibility(env_create_opts)
9
+ end
10
+
11
+ def deploy(version_label, env_settings, inactive_settings)
12
+ @component.new_eb_env.deploy(version_label, env_settings)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,20 @@
1
+ require 'eb_deployer/deployment_strategy/inplace_update'
2
+ require 'eb_deployer/deployment_strategy/blue_green'
3
+ require 'eb_deployer/deployment_strategy/blue_only'
4
+
5
+ module EbDeployer
6
+ module DeploymentStrategy
7
+ def self.create(component, strategy_name)
8
+ case strategy_name.to_s
9
+ when 'inplace_update', 'inplace-update'
10
+ InplaceUpdate.new(component)
11
+ when 'blue_green', 'blue-green'
12
+ BlueGreen.new(component)
13
+ when 'blue_only', 'blue-only'
14
+ BlueOnly.new(component)
15
+ else
16
+ raise 'strategy_name: ' + strategy_name.to_s + ' not supported'
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,204 @@
1
+ module EbDeployer
2
+ class EbEnvironment
3
+ include Utils
4
+
5
+ attr_reader :app, :name
6
+ attr_writer :event_poller
7
+
8
+ def self.unique_ebenv_name(env_name, app_name)
9
+ raise "Environment name #{env_name} is too long, it must be under 32 chars" if env_name.size > 32
10
+ digest = Digest::SHA1.hexdigest(app_name + '-' + env_name)[0..6]
11
+ "#{env_name}-#{digest}"
12
+ end
13
+
14
+ def initialize(app, name, eb_driver, creation_opts={})
15
+ @app = app
16
+ @name = self.class.unique_ebenv_name(name, app)
17
+ @bs = eb_driver
18
+ @creation_opts = default_create_options.merge(reject_nil(creation_opts))
19
+ @accepted_healthy_states = @creation_opts[:accepted_healthy_states]
20
+ @event_poller = nil
21
+ end
22
+
23
+ def deploy(version_label, settings={})
24
+ terminate if @creation_opts[:phoenix_mode]
25
+
26
+ if @bs.environment_exists?(@app, @name)
27
+ update_eb_env(settings, version_label)
28
+ else
29
+ create_eb_env(settings, version_label)
30
+ end
31
+
32
+ smoke_test
33
+ wait_for_env_become_healthy
34
+ end
35
+
36
+ def apply_settings(settings)
37
+ raise "Env #{self.name} not exists for applying settings" unless @bs.environment_exists?(@app, @name)
38
+ wait_for_env_status_to_be_ready
39
+ with_polling_events(/Successfully deployed new configuration to environment/i) do
40
+ @bs.update_environment_settings(@app, @name, settings)
41
+ end
42
+ end
43
+
44
+ def cname_prefix
45
+ @bs.environment_cname_prefix(@app, @name)
46
+ end
47
+
48
+ def swap_cname_with(another)
49
+ log("Swap CNAME with env #{another.name}")
50
+ with_polling_events(/Completed swapping CNAMEs for environments/i) do
51
+ @bs.environment_swap_cname(self.app, self.name, another.name)
52
+ end
53
+ end
54
+
55
+ def log(msg)
56
+ puts "[#{Time.now.utc}][environment:#{@name}] #{msg}"
57
+ end
58
+
59
+ def terminate
60
+ if @bs.environment_exists?(@app, @name)
61
+ with_polling_events(/terminateEnvironment completed successfully/i) do
62
+ @bs.delete_environment(@app, @name)
63
+ end
64
+ end
65
+ end
66
+
67
+ def health_state
68
+ @bs.environment_health_state(@app, @name)
69
+ end
70
+
71
+ private
72
+
73
+ def configured_tier
74
+ @creation_opts[:tier]
75
+ end
76
+
77
+ def template_name
78
+ @creation_opts[:template_name]
79
+ end
80
+
81
+ def has_cname?
82
+ !configured_tier || configured_tier.downcase == 'webserver'
83
+ end
84
+
85
+ def configured_cname_prefix
86
+ @creation_opts[:cname_prefix]
87
+ end
88
+
89
+ def create_eb_env(settings, version_label)
90
+ solution_stack = @creation_opts[:solution_stack]
91
+ tags = convert_tags_hash_to_array(@creation_opts.delete(:tags))
92
+ validate_solutions_stack(solution_stack)
93
+ with_polling_events(/Successfully launched environment/i) do
94
+ @bs.create_environment(@app,
95
+ @name,
96
+ solution_stack,
97
+ has_cname? ? configured_cname_prefix : nil,
98
+ version_label,
99
+ configured_tier,
100
+ tags,
101
+ settings,
102
+ template_name)
103
+ end
104
+ end
105
+
106
+ def update_eb_env(settings, version_label)
107
+ with_polling_events(/Successfully deployed new configuration to environment/i) do
108
+ @bs.update_environment(@app,
109
+ @name,
110
+ version_label,
111
+ configured_tier,
112
+ settings,
113
+ template_name)
114
+ end
115
+ end
116
+
117
+ def validate_solutions_stack(stack_name)
118
+ names = @bs.list_solution_stack_names
119
+ raise "'#{stack_name}' is not a valid solution stack name, available solution stack names are: #{names.join(', ')}" unless names.include?(stack_name)
120
+ end
121
+
122
+ def smoke_test
123
+ host_name = @bs.environment_cname(@app, @name)
124
+ SmokeTest.new(@creation_opts[:smoke_test]).run(host_name, self)
125
+ end
126
+
127
+ def with_polling_events(terminate_pattern, &block)
128
+ anchor = event_poller.get_anchor
129
+ yield
130
+ event_poller.poll(anchor) do |event|
131
+ if event[:message] =~ /Failed to deploy application/
132
+ raise event[:message]
133
+ end
134
+
135
+ if event[:message] =~ /Command failed on instance/
136
+ raise "Elasticbeanstalk instance provision failed (maybe a problem with your .ebextension files). The original message: #{event[:message]}"
137
+ end
138
+
139
+ if event[:message] =~ /complete, but with errors/
140
+ raise event[:message]
141
+ end
142
+
143
+ if event[:message] =~ /However, there were issues during launch\. See event log for details\./
144
+ raise "Environment launched, but with errors. The original message: #{event[:message]}"
145
+ end
146
+
147
+ log_event(event)
148
+ break if event[:message] =~ terminate_pattern
149
+ end
150
+ end
151
+
152
+ def convert_tags_hash_to_array tags
153
+ tags ||= {}
154
+ tags.inject([]) do |arr, (k, v)|
155
+ arr << {:key => k, :value => v}
156
+ arr
157
+ end
158
+ end
159
+
160
+ def wait_for_env_status_to_be_ready
161
+ Timeout.timeout(600) do
162
+ current_status = @bs.environment_status(@app, @name)
163
+
164
+ while current_status.downcase != 'ready'
165
+ log("Environment status: #{current_status}")
166
+ sleep 15
167
+ current_status = @bs.environment_status(@app, @name)
168
+ end
169
+
170
+ log("Environment status: #{current_status}")
171
+ end
172
+ end
173
+
174
+ def wait_for_env_become_healthy
175
+ Timeout.timeout(600) do
176
+ current_health_status = @bs.environment_health_state(@app, @name)
177
+ while !@accepted_healthy_states.include?(current_health_status)
178
+ log("health status: #{current_health_status}")
179
+ sleep 15
180
+ current_health_status = @bs.environment_health_state(@app, @name)
181
+ end
182
+
183
+ log("health status: #{current_health_status}")
184
+ end
185
+ end
186
+
187
+ def event_poller
188
+ @event_poller || EventPoller.new(EbEventSource.new(@app, @name, @bs))
189
+ end
190
+
191
+ def default_create_options
192
+ {
193
+ :solution_stack => "64bit Amazon Linux 2014.09 v1.1.0 running Tomcat 7 Java 7",
194
+ :smoke_test => Proc.new {},
195
+ :tier => 'WebServer',
196
+ :accepted_healthy_states => ['Green']
197
+ }
198
+ end
199
+
200
+ def log_event(event)
201
+ puts "[#{event[:event_date]}][environment:#{@name}] #{event[:message]}"
202
+ end
203
+ end
204
+ end
@@ -0,0 +1,35 @@
1
+ module EbDeployer
2
+ class EbEventSource
3
+ def initialize(app, env, eb_driver)
4
+ @app, @env, @eb_driver = app, env, eb_driver
5
+ end
6
+
7
+ def get_anchor
8
+ events, _ = fetch_events_from_eb(:max_records => 1)
9
+ events.first
10
+ end
11
+
12
+ def fetch_events(from_anchor, &block)
13
+ options = {}
14
+ if from_anchor && from_anchor[:event_date]
15
+ options[:start_time] = from_anchor[:event_date].iso8601
16
+ end
17
+ events, next_token = fetch_events_from_eb(options)
18
+ should_continue = yield(events)
19
+ fetch_next(next_token, &block) if next_token && should_continue
20
+ end
21
+
22
+ private
23
+
24
+ def fetch_next(next_token, &block)
25
+ events, next_token = fetch_events_from_eb(:next_token => next_token)
26
+ should_continue = yield(events)
27
+ fetch_next(next_token, &block) if next_token && should_continue
28
+ end
29
+
30
+ def fetch_events_from_eb(options)
31
+ @eb_driver.fetch_events(@app, @env, options)
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,60 @@
1
+ module EbDeployer
2
+ class Environment
3
+ include Utils
4
+ attr_accessor :creation_opts, :strategy_name
5
+
6
+ attr_writer :resource_stacks, :settings, :inactive_settings, :component_under_deploy
7
+
8
+ attr_reader :name
9
+
10
+ def initialize(app, name, stack_name, eb_driver, &block)
11
+ @app = app
12
+ @name = name
13
+ @stack_name = stack_name
14
+ @eb_driver = eb_driver
15
+ @creation_opts = {}
16
+ @settings = []
17
+ @inactive_settings = []
18
+ @strategy_name = :blue_green
19
+ @components = nil
20
+ yield(self) if block_given?
21
+ unless @components
22
+ @components = [DefaultComponent.new(self, @creation_opts, @strategy_name, @eb_driver)]
23
+ end
24
+ end
25
+
26
+ def app_name
27
+ @app.name
28
+ end
29
+
30
+ def deploy(version_label)
31
+ resource_settings = @resource_stacks.provision(@stack_name)
32
+ components_to_deploy.each do |component|
33
+ component.deploy(version_label, @settings + resource_settings, @inactive_settings)
34
+ end
35
+ end
36
+
37
+ def components=(components_attrs)
38
+ return unless components_attrs
39
+ @components = components_attrs.map do |attrs|
40
+ attrs = symbolize_keys(attrs)
41
+ Component.new(attrs.delete(:name), self, attrs, @eb_driver)
42
+ end
43
+ end
44
+
45
+ private
46
+ def components_to_deploy
47
+ if @component_under_deploy
48
+ component = component_named(@component_under_deploy)
49
+ raise "'#{@component_under_deploy}' is not in the configuration. Available components are #{@components.map(&:name) }" unless component
50
+ [component]
51
+ else
52
+ @components
53
+ end
54
+ end
55
+
56
+ def component_named(name)
57
+ @components.detect { |c| c.name == name }
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,51 @@
1
+ module EbDeployer
2
+ class EventPoller
3
+ include Utils
4
+ POLL_INTERVAL = 15
5
+
6
+ def initialize(event_source)
7
+ @event_source = event_source
8
+ end
9
+
10
+ def get_anchor
11
+ @event_source.get_anchor
12
+ end
13
+
14
+ def poll(from_anchor, &block)
15
+ handled = Set.new
16
+ loop do
17
+ @event_source.fetch_events(from_anchor) do |events|
18
+ # events from event source is latest first order
19
+ to_be_handled = []
20
+ reached_anchor = false
21
+
22
+ events.each do |event|
23
+ if digest(event) == digest(from_anchor)
24
+ reached_anchor = true
25
+ end
26
+
27
+ if !handled.include?(digest(event)) && !reached_anchor
28
+ to_be_handled << event
29
+ end
30
+ end
31
+
32
+ to_be_handled.reverse.each do |event|
33
+ yield(event)
34
+ handled << digest(event)
35
+ end
36
+
37
+ !reached_anchor
38
+ end
39
+ sleep POLL_INTERVAL
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ def digest(event)
46
+ return nil unless event
47
+ event = event.to_h if event.respond_to?(:to_h)
48
+ event.to_json
49
+ end
50
+ end
51
+ end