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,181 @@
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: <%= solution_stack_name %>
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 two deployment strategies: 'blue-green' or 'inplace-update'.
16
+ # Blue green deployments keep two elastic beanstalk environments and always deploy to
17
+ # inactive one, to achieve zero downtime.
18
+ # Inplace-update strategy will only keep one environment, and update the version inplace on
19
+ # deploy. Inplace-update will save resources but will suffer from downtime.
20
+ # (All old environments need be destroyed when you switching between strategies.)
21
+ # Default strategy is 'blue-green'.
22
+ # strategy: blue-green
23
+
24
+ # Name of s3 bucket where uploaded application packages will be stored.
25
+ # Note that the string ".packages" will be added as a suffix to your bucket.
26
+ # So, if "thoughtworks.simple" is passed as the bucket name, the actual s3 bucket
27
+ # name will be thoughtworks.simple.packages. Default to application name.
28
+ # package_bucket: my-s3-bucket
29
+
30
+ # If phoenix mode is turned 'on', it will terminate the old elastic
31
+ # beanstalk environment and recreate a new one on deploy. For blue-green
32
+ # deployment it will terminate the inactive environment first then
33
+ # recreate it. This is useful to avoid configuration drift and
34
+ # accumulating state on the ec2 instances. Also it has the benefit of
35
+ # keeping your ec2 instance system package upto date, because everytime ec2
36
+ # instance boots up from AMI it does a system update. Default is 'off' but we suggest
37
+ # you override it to 'on' for production environment.
38
+ # phoenix_mode: false
39
+
40
+ # Terminates the inactive stack upon successful blue-green deployment.
41
+ # This is useful if you require HA deployment but don't want the cost of running
42
+ # a stack that is not in use. See https://github.com/ThoughtWorksStudios/eb_deployer/pull/44/
43
+ # for other strategies that can be employed.
44
+ # Note: As EB Deployer uses DNS to switch between stacks, this should be used only if you
45
+ # both put in a sufficient wait period and accept the risk that traffic may still be
46
+ # going to the inactive stack when it is terminated, due to client DNS caching, for example.
47
+ #blue_green_terminate_inactive: false
48
+
49
+ # How long to wait before terminating the inactive stack if 'blue_green_terminate_inactive' is set to true.
50
+ #blue_green_terminate_inactive_wait: 600
51
+
52
+ # How often to check for changes to the inactive stack during the 'blue_green_terminate_inactive_wait' period.
53
+ #blue_green_terminate_inactive_sleep: 15
54
+
55
+ # Specifies the maximum number of versions to keep. Older versions are removed
56
+ # and deleted from the S3 source bucket as well. If specified as zero or not
57
+ # specified, all versions will be kept. If a version_prefix is given, only removes
58
+ # version starting with the prefix.
59
+ # keep_latest: 200
60
+
61
+ # Specifies a prefix to prepend to the version label.
62
+ # This can be useful if you want to use different binaries for different
63
+ # environments.
64
+ # version_prefix:
65
+
66
+ # Generating version label for package to be deployed. A readable version label will
67
+ # provide better traceablity of your deployment process.
68
+ # By default setting is:
69
+ # version_label: <%%= package_digest %>
70
+ # which means using MD5 digest of the package file. If you deploy using build
71
+ # pipeline tool such as GO, switching to pipline counter is highly suggested to
72
+ # increase the readability. Following example will read pipeline counter from environment
73
+ # variable with a fall back to digest for local deployment:
74
+ # version_label: <%%= ENV['GO_PIPELINE_COUNTER'] || package_digest %>
75
+
76
+
77
+ # Smoke test value should be a piece of ruby code with access to single variable
78
+ # "host_name" -- environment DNS name. Smoke test snippet will be evaluated at
79
+ # the end of the deployment for inplace-update deployment. For blue-green
80
+ # deployment it will run after inactive environment update is completed and before
81
+ # switching over.
82
+ # Defining a smoke test is highly recommended for serious usage. By default we use
83
+ # The simplest one that just be checking server landing page using curl, e.g.
84
+ smoke_test: |
85
+ curl_http_code = "curl -s -o /dev/null -w \"%{http_code}\" http://#{host_name}"
86
+ Timeout.timeout(600) do
87
+ until ['200', '301', '302'].include?(`#{curl_http_code}`.strip)
88
+ sleep 5
89
+ end
90
+ end
91
+
92
+ # List of Elastic Beanstalk health states that will be considered healthy when deploying.
93
+ # You may want to override default 'Green' with that list, if for example you are using
94
+ # Elastic Beanstalk worker tier and you don't want eb_deployer to fail if you have messages
95
+ # in your SQS queue.
96
+ # By default eb_deployer only considers 'Green' as the healthy state.
97
+ # For a list of all Elastic Beanstalk health states refer to:
98
+ # http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/using-features.healthstatus.html#using-features.healthstatus.colors
99
+ # accepted_healthy_states:
100
+ # - Green
101
+ # - Yellow
102
+
103
+ # Elastic Beanstalk settings that will apply to the environments you are
104
+ # deploying.
105
+ # For all available options take a look at
106
+ # http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/command-options.html
107
+ option_settings:
108
+ # Following is an example of set EC2 ssh key name. This allow you ssh into the ec2
109
+ # instance. See http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-key-pairs.html
110
+ # - namespace: aws:autoscaling:launchconfiguration
111
+ # option_name: EC2KeyName
112
+ # value: <my-ec2-key-name>
113
+
114
+ # Following is an example which changes EC2 instance type
115
+ # from t1.micro (default) to m1.small. Instances with t1.micro type sometime
116
+ # are not very responsible, so m1.small is suggested for saving time.
117
+ # But if you care about the marginal cost difference you can comment this out to
118
+ # go back to t1.micro.
119
+ - namespace: aws:autoscaling:launchconfiguration
120
+ option_name: InstanceType
121
+ value: m1.small
122
+ - namespace: aws:elasticbeanstalk:application:environment
123
+ option_name: DATABASE_NAME
124
+ value: <%= alphanumeric_name %>
125
+ - namespace: aws:elasticbeanstalk:application:environment
126
+ option_name: DATABASE_USERNAME
127
+ value: <%= alphanumeric_name %>
128
+ - namespace: aws:elasticbeanstalk:application:environment
129
+ option_name: DATABASE_PASSWORD
130
+ value: <%= db_password %>
131
+
132
+
133
+ # If resources specified, eb_deployer will use the CloudFormation
134
+ # template you provide to create a default CloudFormation stack with
135
+ # name <application_name>-<env-name> for the environment current
136
+ # deploying. And Outputs of the CloudFormation can be mapped to Elastic Beanstalk
137
+ # options settings.
138
+ # keys:
139
+ # template => CloudFormation template file with JSON format
140
+ # policy => CloudFormation policy file with JSON format
141
+ # 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,
142
+ # otherwise the provided policy will replace any existing policy on the resource stack
143
+ # inputs => A Hash, input values for the CloudFormation template
144
+ # outputs => A Hash with key map to your CloudFormation template outputs and value as elastic beanstalk settings namespace and option_name.
145
+ # capabilities => An array. You need set it to ['CAPABILITY_IAM'] if the
146
+ # template include IAM Instance Profile.
147
+ resources:
148
+ # Creating a RDS instance by CloudFormation, so that database will not hook up with any ElasticBeanstalk environment
149
+ template: config/rds.json
150
+ inputs:
151
+ DBName: <%= alphanumeric_name %>
152
+ DBUser: <%= alphanumeric_name %>
153
+ DBPassword: <%= db_password %>
154
+ outputs:
155
+ RDSPassSecurityGroup:
156
+ namespace: aws:autoscaling:launchconfiguration
157
+ option_name: SecurityGroups
158
+ RDSHost:
159
+ namespace: aws:elasticbeanstalk:application:environment
160
+ option_name: DATABASE_HOST
161
+ RDSPort:
162
+ namespace: aws:elasticbeanstalk:application:environment
163
+ option_name: DATABASE_PORT
164
+
165
+ # You can define environment here. Each environment can overriden any common settings
166
+ environments:
167
+ dev:
168
+ # overriding common settings
169
+ strategy: inplace-update
170
+ production:
171
+ option_settings:
172
+ # example for overriding common option_settings: providing least redundancy
173
+ # in production environment.
174
+ # - namespace: aws:autoscaling:asg
175
+ # option_name: MinSize
176
+ # value: "2"
177
+
178
+ # For Rails4: config/secrets.yml
179
+ - namespace: aws:elasticbeanstalk:application:environment
180
+ option_name: SECRET_KEY_BASE
181
+ value: <%= secure_random(128) %>
@@ -0,0 +1,5 @@
1
+ ---
2
+ packages:
3
+ yum:
4
+ postgresql93-libs: []
5
+ postgresql93-devel: []
@@ -0,0 +1,88 @@
1
+ {
2
+ "Outputs": {
3
+ "RDSHost": {
4
+ "Description": "Database endpoint address",
5
+ "Value": { "Fn::GetAtt": ["RDSDatabase", "Endpoint.Address"] }
6
+ },
7
+ "RDSPort": {
8
+ "Description": "Database endpoint port",
9
+ "Value": { "Fn::GetAtt": ["RDSDatabase", "Endpoint.Port"] }
10
+ },
11
+ "RDSPassSecurityGroup": {
12
+ "Description": "Security group assign to ec2 instance that need access to rds instance",
13
+ "Value": {
14
+ "Ref": "RDSPassSecurityGroup"
15
+ }
16
+ }
17
+ },
18
+
19
+ "Parameters": {
20
+ "DBUser": {
21
+ "NoEcho": "false",
22
+ "Description": "The name of master user for the client DB Instance.",
23
+ "Type": "String",
24
+ "ConstraintDescription": "must begin with a letter and contain only alphanumeric characters"
25
+ },
26
+
27
+ "DBName": {
28
+ "NoEcho": "false",
29
+ "Description": "The DB Name of the RDS instance",
30
+ "Type": "String",
31
+ "ConstraintDescription": "must contain only alphanumeric characters"
32
+ },
33
+
34
+ "DBPassword": {
35
+ "NoEcho": "true",
36
+ "Description": "The master password for the DB instance.",
37
+ "Type": "String",
38
+ "ConstraintDescription": "must contain only alphanumeric characters"
39
+ }
40
+ },
41
+
42
+ "Resources": {
43
+ "RDSDBSecurityGroup": {
44
+ "Type": "AWS::RDS::DBSecurityGroup",
45
+ "Properties": {
46
+ "GroupDescription": "Enable database access to Beanstalk application",
47
+ "DBSecurityGroupIngress": {
48
+ "EC2SecurityGroupName": {
49
+ "Ref": "RDSPassSecurityGroup"
50
+ }
51
+ }
52
+ }
53
+ },
54
+
55
+ "RDSDatabase": {
56
+ "Type": "AWS::RDS::DBInstance",
57
+ "DeletionPolicy": "Delete",
58
+ "Properties": {
59
+ "MasterUsername": {
60
+ "Ref": "DBUser"
61
+ },
62
+ "DBSecurityGroups": [
63
+ {
64
+ "Ref": "RDSDBSecurityGroup"
65
+ }
66
+ ],
67
+ "DBInstanceClass": "db.m1.small",
68
+ "AllocatedStorage": "5",
69
+ "MultiAZ": "false",
70
+ "EngineVersion": "9.3.3",
71
+ "DBName": {
72
+ "Ref": "DBName"
73
+ },
74
+ "MasterUserPassword": {
75
+ "Ref": "DBPassword"
76
+ },
77
+ "Engine": "postgres"
78
+ }
79
+ },
80
+
81
+ "RDSPassSecurityGroup": {
82
+ "Type": "AWS::EC2::SecurityGroup",
83
+ "Properties": {
84
+ "GroupDescription": "SecurityGroup access RDS database."
85
+ }
86
+ }
87
+ }
88
+ }
@@ -0,0 +1,350 @@
1
+ class EBStub
2
+ attr_reader :envs, :events
3
+ def initialize
4
+ @apps = []
5
+ @envs = {}
6
+ @versions = {}
7
+ @envs_been_deleted = {}
8
+ @versions_deleted = {}
9
+ @event_fetched_times = 0
10
+ @envs_health_states = {}
11
+ @events = nil
12
+ end
13
+
14
+ def create_application(app)
15
+ raise 'already exists' if application_exists?(app)
16
+ @apps << app
17
+ end
18
+
19
+ def delete_application(app)
20
+ raise "there are environments running for this app" unless environment_names_for_application(app).empty?
21
+ @apps.delete(app)
22
+ end
23
+
24
+ def application_exists?(app)
25
+ @apps.include?(app)
26
+ end
27
+
28
+ def create_environment(app, env, solution_stack, cname_prefix, version, tier, tags, settings, template_name)
29
+ raise 'cname prefix is not avaible' if @envs.values.detect { |value| value[:cname_prefix] == cname_prefix }
30
+ raise "env name #{env} is longer than 23 chars" if env.size > 23
31
+ raise "app not exists" unless application_exists?(app)
32
+ @envs[env_key(app, env)] = {
33
+ :name => env,
34
+ :application => app,
35
+ :solution_stack => solution_stack,
36
+ :version => version,
37
+ :cname_prefix => cname_prefix,
38
+ :tier => tier,
39
+ :tags => tags,
40
+ :settings => settings,
41
+ :template_name => template_name }
42
+ set_env_ready(app, env, false)
43
+ end
44
+
45
+ def delete_environment(app, env)
46
+ @envs.delete(env_key(app, env))
47
+ @envs_been_deleted[app] ||= []
48
+ @envs_been_deleted[app] << env
49
+ end
50
+
51
+ def update_environment_settings(app, env, settings)
52
+ raise "not in ready state, consider waiting for previous action finish by pulling envents" unless env_ready?(app, env)
53
+ @envs[env_key(app, env)].merge!(:settings => settings)
54
+ end
55
+
56
+
57
+ def update_environment(app, env, version, tier, settings, template_name)
58
+ raise "not in ready state, consider waiting for previous action finish by pulling envents" unless env_ready?(app, env)
59
+ @envs[env_key(app, env)].merge!(:version => version, :settings => settings, :tier => tier, :template_name => template_name)
60
+ set_env_ready(app, env, false)
61
+ end
62
+
63
+ def environment_exists?(app_name, env_name)
64
+ @envs.has_key?(env_key(app_name, env_name))
65
+ end
66
+
67
+ def create_application_version(app_name, version_label, source_bundle)
68
+ @versions[app_name] ||= []
69
+ @versions[app_name] << {
70
+ :version_label => version_label,
71
+ :source_bundle => source_bundle,
72
+ :date_created => Time.now,
73
+ :date_updated => Time.now
74
+ }
75
+ end
76
+
77
+ def delete_application_version(app_name, version, delete_source_bundle)
78
+ @versions_deleted[app_name] ||= []
79
+ @versions_deleted[app_name] << version
80
+ @versions[app_name].delete_if { |apv| apv[:version_label] == version }
81
+ end
82
+
83
+ def application_versions(app_name)
84
+ @versions[app_name]
85
+ end
86
+
87
+ def application_version_labels(app_name)
88
+ if @versions[app_name]
89
+ @versions[app_name].map { |appv| appv[:version_label]}
90
+ else
91
+ []
92
+ end
93
+ end
94
+
95
+ # simulating elasticbeanstalk events api behavior
96
+ # use set_events and append_events to set fake events
97
+ def fetch_events(app_name, env_name, options={})
98
+ @event_fetched_times += 1
99
+ set_env_ready(app_name, env_name, true) # assume env become ready after it spit out all the events
100
+
101
+ unless @events # unrestricted mode for testing if no explicit events set
102
+ return [generate_event_from_messages(['Successfully deployed new configuration to environment',
103
+ 'terminateEnvironment completed successfully',
104
+ 'Successfully launched environment',
105
+ 'Completed swapping CNAMEs for environments'
106
+ ], Time.now + @event_fetched_times), nil]
107
+ end
108
+
109
+ events = @events[env_key(app_name, env_name)][@event_fetched_times - 1]
110
+
111
+ if options.has_key?(:start_time)
112
+ start_time = Time.parse(options[:start_time])
113
+ events = events.select { |e| e[:event_date] >= start_time }
114
+ end
115
+
116
+ if limit = options[:max_records]
117
+ events = events[0..limit]
118
+ end
119
+
120
+ [events, nil]
121
+ end
122
+
123
+ # add fake events for each times of fetch events call
124
+ # message passed in should be old to new order
125
+ # e.g. given set_events("myapp", "test", ['a'], ['b', 'c'])
126
+ # then
127
+ # fetch_events('myapp', 'test') # => [{message: 'a'}] for first time
128
+ # fetch_events('myapp', 'test') # => [{message: 'c'}, {message: 'b'}, {message: 'a'}] for the second time call
129
+ def set_events(app_name, env_name, *messages)
130
+ events_seq = []
131
+ messages.each do |messages_for_call_seq|
132
+ if old_events = events_seq.last
133
+ last_event_date = old_events.first && old_events.first[:event_date]
134
+ events_seq << (generate_event_from_messages(messages_for_call_seq, last_event_date) + old_events)
135
+ else
136
+ events_seq << generate_event_from_messages(messages_for_call_seq)
137
+ end
138
+ end
139
+ @events ||= {}
140
+ @events[env_key(app_name, env_name)] = events_seq
141
+ end
142
+
143
+ def environment_cname_prefix(app_name, env_name)
144
+ return unless @envs[env_key(app_name, env_name)]
145
+ @envs[env_key(app_name, env_name)][:cname_prefix]
146
+ end
147
+
148
+ def environment_cname(app_name, env_name)
149
+ return unless @envs[env_key(app_name, env_name)]
150
+ if cname_prefix = environment_cname_prefix(app_name, env_name)
151
+ cname_prefix + ".us-west-1.elasticbeanstalk.com"
152
+ end
153
+ end
154
+
155
+
156
+ def environment_swap_cname(app_name, env1_name, env2_name)
157
+ raise "#{env1_name} not in ready state, consider to wait for previous action finsish by pulling events" unless env_ready?(app_name, env1_name)
158
+
159
+ env1, env2 = @envs[env_key(app_name, env1_name)], @envs[env_key(app_name, env2_name)]
160
+ temp = env1[:cname_prefix]
161
+ env1[:cname_prefix] = env2[:cname_prefix]
162
+ env2[:cname_prefix] = temp
163
+ set_env_ready(app_name, env1_name, false)
164
+ set_env_ready(app_name, env2_name, false)
165
+ end
166
+
167
+ def environment_status(app_name, env_name)
168
+ 'ready'
169
+ end
170
+
171
+ def environment_health_state(app_name, env_name)
172
+ @envs_health_states.fetch(app_name+env_name, 'Green')
173
+ end
174
+
175
+ def environment_verion_label(app_name, env_name)
176
+ @envs[env_key(app_name, env_name)][:version]
177
+ end
178
+
179
+ def list_solution_stack_names
180
+ ["64bit Amazon Linux 2014.09 v1.1.0 running Tomcat 7 Java 7"]
181
+ end
182
+
183
+ #test only
184
+
185
+ def mark_env_health_state_as(app_name, env_name, state)
186
+ @envs_health_states[app_name+env_name] = state
187
+ end
188
+
189
+ def mark_all_envs_ready
190
+ @envs.values.each { |env| set_env_ready(env[:application], env[:name], true) }
191
+ end
192
+
193
+ def set_solution_stacks(names)
194
+ @solution_stacks = names
195
+ end
196
+
197
+ def environment_tier(app_name, env_name)
198
+ @envs[env_key(app_name, env_name)][:tier]
199
+ end
200
+
201
+ def environment_tags(app_name, env_name)
202
+ @envs[env_key(app_name, env_name)][:tags]
203
+ end
204
+
205
+ def environment_settings(app_name, env_name)
206
+ @envs[env_key(app_name, env_name)][:settings]
207
+ end
208
+
209
+ def environment_names_for_application(app)
210
+ @envs.inject([]) do |memo, pair|
211
+ _, env = pair
212
+ memo << env[:name] if env[:application] == app
213
+ memo
214
+ end
215
+ end
216
+
217
+ def environments_been_deleted(app)
218
+ @envs_been_deleted[app] || []
219
+ end
220
+
221
+ def versions_deleted(app_name)
222
+ @versions_deleted[app_name]
223
+ end
224
+
225
+ def template_name(app_name, env_name)
226
+ @envs[env_key(app_name, env_name)][:template_name]
227
+ end
228
+
229
+ private
230
+
231
+ def set_env_ready(app, env, ready)
232
+ if record = @envs[env_key(app, env)]
233
+ record[:ready] = ready
234
+ end
235
+ end
236
+
237
+ def env_ready?(app, env)
238
+ @envs[env_key(app, env)][:ready]
239
+ end
240
+
241
+ def generate_event_from_messages(messages, start_time=Time.now)
242
+ start_time ||= Time.now
243
+ events = messages.map do |m|
244
+ { :message => m }
245
+ end
246
+
247
+ events.each_with_index do |e, i|
248
+ e[:event_date] = start_time + (i + 1)
249
+ end
250
+
251
+ events.reverse
252
+ end
253
+
254
+ def env_key(app, name)
255
+ [app, name].join("-")
256
+ end
257
+
258
+ end
259
+
260
+ class S3Stub
261
+ def initialize
262
+ @buckets = {}
263
+ end
264
+
265
+ def create_bucket(bucket_name)
266
+ @buckets[bucket_name] = {}
267
+ end
268
+
269
+ def bucket_exists?(bucket_name)
270
+ @buckets.has_key?(bucket_name)
271
+ end
272
+
273
+ def objects(bucket_name)
274
+ @buckets[bucket_name]
275
+ end
276
+
277
+ def object_length(bucket_name, obj_name)
278
+ @buckets[bucket_name][obj_name] && File.size(@buckets[bucket_name][obj_name])
279
+ end
280
+
281
+ def upload_file(bucket_name, obj_name, file)
282
+ @buckets[bucket_name][obj_name] = file
283
+ end
284
+
285
+ end
286
+
287
+ class CFStub
288
+ def initialize
289
+ @stacks = {}
290
+ @events = {}
291
+ @event_fetch_counter = 0;
292
+ end
293
+
294
+ def create_stack(name, template, opts)
295
+ @stacks[name] = {:template => template, :opts => opts }
296
+ end
297
+
298
+ def update_stack(name, template, opts)
299
+ @stacks[name] = @stacks[name].merge(:template => template, :opts => opts)
300
+ end
301
+
302
+ def stack_status(name)
303
+ @stacks[name] && :update_complete
304
+ end
305
+
306
+ def stack_exists?(name)
307
+ @stacks.has_key?(name)
308
+ end
309
+
310
+ def query_output(name, key)
311
+ raise Aws::CloudFormation::Errors::ValidationError.new(nil, nil) unless stack_exists?(name)
312
+ "value of #{key}"
313
+ end
314
+
315
+ def stack_config(name)
316
+ @stacks[name][:opts]
317
+ end
318
+
319
+ def set_events(name, *messages)
320
+ events_seq = []
321
+ messages.each do |messages_for_call_seq|
322
+ if old_events = events_seq.last
323
+ events_seq << generate_event_from_messages(name, messages_for_call_seq) + old_events
324
+ else
325
+ events_seq << generate_event_from_messages(name, messages_for_call_seq)
326
+ end
327
+ end
328
+ @events[name] = events_seq
329
+ end
330
+
331
+ def fetch_events(name, opts={})
332
+ @event_fetch_counter += 1
333
+ if es = @events[name]
334
+ return es[@event_fetch_counter - 1], nil
335
+ else
336
+ return generate_event_from_messages(name, ["UPDATE_COMPLETE"]), nil
337
+ end
338
+ end
339
+
340
+ private
341
+
342
+ def generate_event_from_messages(stack, messages)
343
+ messages.map do |message|
344
+ OpenStruct.new(timestamp: Time.now,
345
+ resource_type: 'AWS::CloudFormation::Stack',
346
+ logical_resource_id: stack,
347
+ resource_status: message)
348
+ end.reverse
349
+ end
350
+ end
@@ -0,0 +1,23 @@
1
+ require 'test_helper'
2
+
3
+ class BeanstalkTest < Test::Unit::TestCase
4
+
5
+ class StubbedCnameResponder
6
+ def initialize(stubbed_response)
7
+ @stubbed_response = stubbed_response
8
+ end
9
+
10
+ def describe_environments(options)
11
+ {:environments => [@stubbed_response.merge(:status => "online")]}
12
+ end
13
+ end
14
+
15
+ def test_cname_prefix_will_parse_both_legacy_and_regionalized_domains
16
+ legacy_domain = "mingle-saas.elasticbeanstalk.com"
17
+ regionalized_domain = "mingle-saas.us-west-1.elasticbeanstalk.com"
18
+
19
+ assert_equal "mingle-saas", EbDeployer::AWSDriver::Beanstalk.new(StubbedCnameResponder.new(:cname => legacy_domain)).environment_cname_prefix("mingle", "saas")
20
+ assert_equal "mingle-saas", EbDeployer::AWSDriver::Beanstalk.new(StubbedCnameResponder.new(:cname => regionalized_domain)).environment_cname_prefix("mingle", "saas")
21
+ end
22
+
23
+ end