man_eb_deployer 0.8.0

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