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.
- 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
|