man_eb_deployer 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/workflows/release.yml +31 -0
- data/.github/workflows/test.yml +16 -0
- data/.gitignore +12 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +143 -0
- data/Gemfile +10 -0
- data/LICENSE +22 -0
- data/README.md +138 -0
- data/Rakefile +12 -0
- data/TODOS.md +11 -0
- data/bin/eb_deploy +13 -0
- data/eb_deployer.gemspec +22 -0
- data/lib/eb_deployer/application.rb +96 -0
- data/lib/eb_deployer/aws_driver/beanstalk.rb +158 -0
- data/lib/eb_deployer/aws_driver/cloud_formation_driver.rb +53 -0
- data/lib/eb_deployer/aws_driver/s3_driver.rb +35 -0
- data/lib/eb_deployer/aws_driver.rb +8 -0
- data/lib/eb_deployer/cf_event_source.rb +26 -0
- data/lib/eb_deployer/cloud_formation_provisioner.rb +120 -0
- data/lib/eb_deployer/component.rb +45 -0
- data/lib/eb_deployer/config_loader.rb +64 -0
- data/lib/eb_deployer/default_component.rb +32 -0
- data/lib/eb_deployer/default_config.rb +20 -0
- data/lib/eb_deployer/default_config.yml +159 -0
- data/lib/eb_deployer/deployment_strategy/blue_green.rb +79 -0
- data/lib/eb_deployer/deployment_strategy/blue_only.rb +45 -0
- data/lib/eb_deployer/deployment_strategy/inplace_update.rb +16 -0
- data/lib/eb_deployer/deployment_strategy.rb +20 -0
- data/lib/eb_deployer/eb_environment.rb +204 -0
- data/lib/eb_deployer/eb_event_source.rb +35 -0
- data/lib/eb_deployer/environment.rb +60 -0
- data/lib/eb_deployer/event_poller.rb +51 -0
- data/lib/eb_deployer/package.rb +39 -0
- data/lib/eb_deployer/resource_stacks.rb +20 -0
- data/lib/eb_deployer/smoke_test.rb +23 -0
- data/lib/eb_deployer/tasks.rb +45 -0
- data/lib/eb_deployer/throttling_handling.rb +17 -0
- data/lib/eb_deployer/utils.rb +33 -0
- data/lib/eb_deployer/version.rb +3 -0
- data/lib/eb_deployer/version_cleaner.rb +30 -0
- data/lib/eb_deployer.rb +339 -0
- data/lib/generators/eb_deployer/install/install_generator.rb +82 -0
- data/lib/generators/eb_deployer/install/templates/eb_deployer.rake +1 -0
- data/lib/generators/eb_deployer/install/templates/eb_deployer.yml.erb +181 -0
- data/lib/generators/eb_deployer/install/templates/ebextensions/01_postgres_packages.config +5 -0
- data/lib/generators/eb_deployer/install/templates/postgres_rds.json +88 -0
- data/test/aws_driver_stubs.rb +350 -0
- data/test/beanstalk_test.rb +23 -0
- data/test/blue_green_deploy_test.rb +114 -0
- data/test/blue_only_deploy_test.rb +78 -0
- data/test/cf_event_poller_test.rb +32 -0
- data/test/cloud_formation_provisioner_test.rb +47 -0
- data/test/config_loader_test.rb +205 -0
- data/test/deploy_test.rb +42 -0
- data/test/eb_environment_test.rb +120 -0
- data/test/eb_event_poller_test.rb +32 -0
- data/test/inplace_update_deploy_test.rb +110 -0
- data/test/multi_components_deploy_test.rb +164 -0
- data/test/rails_generators_test.rb +67 -0
- data/test/resources_deploy_test.rb +191 -0
- data/test/smoke_test_test.rb +23 -0
- data/test/template_deploy_test.rb +13 -0
- data/test/test_helper.rb +68 -0
- data/test/tier_setting_deploy_test.rb +24 -0
- data/test/versions_deploy_test.rb +120 -0
- 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,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
|