elastic_beans 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +91 -16
- data/exe/beans +26 -20
- data/lib/elastic_beans/application.rb +33 -1
- data/lib/elastic_beans/application_version.rb +18 -3
- data/lib/elastic_beans/aws/cloudformation_stack.rb +2 -0
- data/lib/elastic_beans/command/configure.rb +12 -18
- data/lib/elastic_beans/command/create.rb +8 -4
- data/lib/elastic_beans/command/deploy.rb +3 -3
- data/lib/elastic_beans/command/exec.rb +4 -3
- data/lib/elastic_beans/command/scale.rb +2 -2
- data/lib/elastic_beans/command/set_env.rb +4 -3
- data/lib/elastic_beans/command/talk.rb +1 -0
- data/lib/elastic_beans/command/version.rb +1 -0
- data/lib/elastic_beans/command.rb +2 -0
- data/lib/elastic_beans/configuration_template/base.rb +12 -2
- data/lib/elastic_beans/configuration_template/exec.rb +6 -0
- data/lib/elastic_beans/configuration_template/scheduler.rb +4 -0
- data/lib/elastic_beans/configuration_template/webserver.rb +11 -4
- data/lib/elastic_beans/configuration_template/worker.rb +6 -0
- data/lib/elastic_beans/configuration_template.rb +32 -11
- data/lib/elastic_beans/dns_entry.rb +2 -0
- data/lib/elastic_beans/env_vars.rb +6 -0
- data/lib/elastic_beans/environment/exec.rb +13 -4
- data/lib/elastic_beans/environment/scheduler.rb +13 -4
- data/lib/elastic_beans/environment/webserver.rb +13 -4
- data/lib/elastic_beans/environment/worker.rb +15 -4
- data/lib/elastic_beans/environment.rb +74 -18
- data/lib/elastic_beans/error/environments_not_ready.rb +2 -0
- data/lib/elastic_beans/error.rb +1 -0
- data/lib/elastic_beans/exec/init.rb +2 -0
- data/lib/elastic_beans/exec/sqs_consumer.rb +2 -0
- data/lib/elastic_beans/network.rb +1 -0
- data/lib/elastic_beans/rack/exec.rb +5 -0
- data/lib/elastic_beans/ui.rb +2 -0
- data/lib/elastic_beans/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2b231170798c366604081d7d0800c0c332a386d6
|
4
|
+
data.tar.gz: 661c9aca9b2ae4fdc6402cd02c5b3d37c4f62d76
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 71da843ae45cfac1cf6fa2061df76383849d16cdb359c1ff7145975c25042ca8985d7501b2e57d09d2968d4b8b0246cf40f89ef73d98b6a165b4cb15ae17f8fa
|
7
|
+
data.tar.gz: 928d9e71798950eb693d0a9ad4a9ac720ca48e56de71b8cf074d0fd409e2c957119d2dd83fcc6dd8b757ef122e08bda185ff25fad6dacddedcf5db7d327bbfb0
|
data/README.md
CHANGED
@@ -19,9 +19,10 @@ As the SDK documentation suggests, using environment variables is recommended.
|
|
19
19
|
[configuration]: http://docs.aws.amazon.com/sdk-for-ruby/latest/DeveloperGuide/aws-ruby-sdk-getting-started.html#aws-ruby-sdk-configuration
|
20
20
|
|
21
21
|
# Pre-configure the application before creating environments
|
22
|
-
|
23
|
-
|
24
|
-
|
22
|
+
beans configure -n myapp-networking -a myapp \
|
23
|
+
-b SECRET_KEY_BASE -d DATABASE_URL -k KEYPAIR \
|
24
|
+
-p INTERNAL_PUBLIC_KEY -s SSL_CERTIFICATE_ARN \
|
25
|
+
[-i IMAGE_ID] [-t INSTANCE_TYPE] [-l LOGGING_HTTP_ENDPOINT]
|
25
26
|
|
26
27
|
# Create a webserver environment with a pretty DNS name at myapp.TLD (managed by Route53)
|
27
28
|
beans create -a myapp [-d myapp.TLD] [--tags=Environment:production Team:Unicorn] webserver
|
@@ -41,25 +42,27 @@ As the SDK documentation suggests, using environment variables is recommended.
|
|
41
42
|
# Set environment variables across all environments
|
42
43
|
beans setenv -a myapp REDIS_URL=redis://...
|
43
44
|
|
44
|
-
#
|
45
|
-
#
|
45
|
+
# Create an application version for the HEAD git commit in the working directory.
|
46
|
+
# Then deploy that version to each running environment.
|
46
47
|
beans deploy -a myapp
|
47
48
|
|
48
|
-
# Run one-off tasks
|
49
|
+
# Run one-off tasks and upload logs to the LOGGING_HTTP_ENDPOINT from `configure`
|
49
50
|
beans exec -a myapp rake db:migrate
|
50
51
|
|
51
52
|
# Update all existing environments and configuration
|
52
|
-
beans configure -n myapp-networking -a myapp
|
53
|
+
beans configure -n myapp-networking -a myapp \
|
54
|
+
[-b SECRET_KEY_BASE] [-d DATABASE_URL] [-k KEYPAIR] \
|
55
|
+
[-p INTERNAL_PUBLIC_KEY] [-s SSL_CERTIFICATE_ARN] \
|
56
|
+
[-i IMAGE_ID] [-t INSTANCE_TYPE] [-l LOGGING_HTTP_ENDPOINT]
|
53
57
|
|
54
58
|
### API
|
55
59
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
app.exec_command("rake db:migrate", sqs: Aws::SQS::Client.new)
|
60
|
+
Elastic Beans exposes the classes it uses to manage Elastic Beanstalk so you can call them from your own code.
|
61
|
+
[Check out the API documentation][api].
|
62
|
+
|
63
|
+
[api]: http://www.rubydoc.info/gems/elastic_beans
|
64
|
+
|
65
|
+
AWS SDK clients must be passed into Elastic Beans constructors.
|
63
66
|
|
64
67
|
### Periodic tasks
|
65
68
|
|
@@ -81,6 +84,7 @@ In `config/initializers/elastic_beans.rb`, add the middleware into your stack, b
|
|
81
84
|
name: "myapp",
|
82
85
|
cloudformation: Aws::CloudFormation::Client.new,
|
83
86
|
elastic_beanstalk: Aws::ElasticBeanstalk::Client.new,
|
87
|
+
s3: Aws::S3::Client.new,
|
84
88
|
),
|
85
89
|
sqs: Aws::SQS::Client.new,
|
86
90
|
logger: Rails.logger,
|
@@ -92,6 +96,7 @@ In `config/initializers/elastic_beans.rb`, add the middleware into your stack, b
|
|
92
96
|
name: "myapp",
|
93
97
|
cloudformation: Aws::CloudFormation::Client.new,
|
94
98
|
elastic_beanstalk: Aws::ElasticBeanstalk::Client.new,
|
99
|
+
s3: Aws::S3::Client.new,
|
95
100
|
),
|
96
101
|
sqs: Aws::SQS::Client.new,
|
97
102
|
logger: Rails.logger,
|
@@ -135,6 +140,12 @@ This way they can be computationally expensive without affecting application per
|
|
135
140
|
Additionally, they are not limited to the visibility timeout of an application background job queue.
|
136
141
|
Logs are persisted to an HTTPS endpoint upon command completion.
|
137
142
|
|
143
|
+
### Persistent configuration
|
144
|
+
|
145
|
+
Elastic Beans stores your configuration in configuration templates.
|
146
|
+
This means that you can terminate and create an environment and it will remember its setup from before.
|
147
|
+
In addition, changes to the configuration will affect existing and future environments.
|
148
|
+
|
138
149
|
### Shared code between environments
|
139
150
|
|
140
151
|
A Rails app supports multiple contexts with the same codebase.
|
@@ -155,7 +166,7 @@ The settings for your application are discovered from the [outputs of these Clou
|
|
155
166
|
|
156
167
|
The networking stack can be shared between many applications, but each application should have its own application stack.
|
157
168
|
For example, you might create separate "eb-development" and "eb-production" network stacks.
|
158
|
-
Then, create an Elastic Beanstalk application from a stack named "myapp."
|
169
|
+
Then, create an Elastic Beanstalk application named "myapp" from a stack named "myapp."
|
159
170
|
Finally, you can set it up with beans:
|
160
171
|
|
161
172
|
$ beans configure -n eb-development -a myapp ...
|
@@ -186,19 +197,83 @@ Elastic Beans will use the default names: `aws-elasticbeanstalk-ec2-role` and `a
|
|
186
197
|
### Application
|
187
198
|
|
188
199
|
Your Elastic Beanstalk application must already exist and also be created using CloudFormation.
|
200
|
+
The Elastic Beanstalk application name and CloudFormation stack name must be identical.
|
189
201
|
Its details will be discovered from the following outputs:
|
190
202
|
|
191
203
|
* `ExecQueueUrl`
|
192
204
|
* `Worker[Name]QueueUrl`
|
193
205
|
|
194
206
|
Where a separate worker environment will be configured for each queue `[Name]` that appears.
|
195
|
-
|
207
|
+
A default worker queue, i.e. `WorkerDefaultQueueUrl`, must exist.
|
196
208
|
|
197
209
|
### Code
|
198
210
|
|
199
211
|
Your application must use the [active-elastic-job gem](https://github.com/tawan/active-elastic-job) for background job processing.
|
200
212
|
Elastic Beans will set the `DISABLE_SQS_CONSUMER` environment variable appropriately in your environments.
|
201
213
|
|
214
|
+
### Known issues and limitations of the pre-release
|
215
|
+
|
216
|
+
Elastic Beans still has some rough edges that need to be worked out.
|
217
|
+
|
218
|
+
#### Authentication with AWS uses environment variables
|
219
|
+
|
220
|
+
Use the `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` environment variables to authenticate with the AWS SDK.
|
221
|
+
|
222
|
+
#### Changes to the configuration in the AWS console are not persistent
|
223
|
+
|
224
|
+
Changing configuration in the AWS console will work fine, but if the setting is also saved in a configuration template it will be overwritten the next time you run `beans configure`.
|
225
|
+
|
226
|
+
#### Creating multiple environments simultaneously causes sample application to be deployed
|
227
|
+
|
228
|
+
Due to a bug in the version-finding code, creating multiple environments simultaneously can cause a sample application to be deployed to one of them (an empty version label).
|
229
|
+
|
230
|
+
#### End-to-end encryption requires some custom setup
|
231
|
+
|
232
|
+
Currently (pre-1.0), you must set up HTTPS yourself in nginx using an ebextension.
|
233
|
+
Use whatever certificate you like to do so, even a self-signed certificate is fine.
|
234
|
+
Use the public key from this certificate as the `--public-key` option to `beans configure`.
|
235
|
+
|
236
|
+
#### Environment variables are mostly hidden
|
237
|
+
|
238
|
+
`beans setenv` sets environment variables in S3, but there's no way to *get* them.
|
239
|
+
|
240
|
+
#### Environment variables are not access-controlled
|
241
|
+
|
242
|
+
Environment variables are stored in plain-text in S3.
|
243
|
+
Anyone with access to the Elastic Beanstalk bucket (for instance, the Elastic Beanstalk instance profile) can read the environment variables.
|
244
|
+
|
245
|
+
#### Rate limiting
|
246
|
+
|
247
|
+
Running several beans tasks concurrently can cause Elastic Beanstalk rate-limiting on the AWS account.
|
248
|
+
|
249
|
+
#### SIGINT causes a stack trace
|
250
|
+
|
251
|
+
Sorry `¯\_(ツ)_/¯`
|
252
|
+
|
253
|
+
#### Solution stack is hard-coded
|
254
|
+
|
255
|
+
If you do not use a custom image, the solution stack will start as a hard-coded value.
|
256
|
+
It will also be up to you to update it.
|
257
|
+
|
258
|
+
#### Uses default Elastic Beanstalk IAM settings and S3 bucket
|
259
|
+
|
260
|
+
Elastic Beans looks for [the default Elastic Beanstalk IAM instance profile and service role][iam].
|
261
|
+
As the linked document explains, the easiest way to create them is to create a sample application in the AWS console.
|
262
|
+
|
263
|
+
Creating the sample application should also create the default Elastic Beanstalk S3 bucket, which has a name of the form `elasticbeanstalk-REGION-ACCOUNT_ID`.
|
264
|
+
Elastic Beans will use this bucket to store application versions and environment variables.
|
265
|
+
|
266
|
+
[iam]: http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/concepts-roles.html
|
267
|
+
|
268
|
+
#### Worker environment health check requires some custom setup
|
269
|
+
|
270
|
+
Elastic Beanstalk worker environments (aws-sqsd) must use a non-SSL health check.
|
271
|
+
Your Rails application must implement this as a middleware (or lower in the stack) before Rails enforces SSL.
|
272
|
+
|
273
|
+
#### Worker environment visibility timeout is hard-coded
|
274
|
+
|
275
|
+
1800 seconds is the longest it can be.
|
276
|
+
|
202
277
|
## Installation
|
203
278
|
|
204
279
|
Add this line to your application's Gemfile:
|
data/exe/beans
CHANGED
@@ -8,27 +8,33 @@ require "elastic_beans/command"
|
|
8
8
|
require "elastic_beans/ui"
|
9
9
|
|
10
10
|
class ElasticBeans::CLI < Thor
|
11
|
+
APPLICATION_DESC = "The name of the Elastic Beanstalk application and CloudFormation stack that contains application settings"
|
12
|
+
|
11
13
|
class_option :verbose, type: :boolean, aliases: %w(-v)
|
12
14
|
|
13
15
|
desc ElasticBeans::Command::Configure::USAGE, ElasticBeans::Command::Configure::DESC
|
14
16
|
long_desc ElasticBeans::Command::Configure::LONG_DESC
|
15
|
-
option :application, aliases: %w(-a), required: true
|
16
|
-
option :network, aliases: %w(-n), required: true
|
17
|
-
option :
|
18
|
-
option :
|
19
|
-
option :
|
20
|
-
option :
|
21
|
-
option :
|
22
|
-
option :
|
17
|
+
option :application, aliases: %w(-a), required: true, desc: APPLICATION_DESC
|
18
|
+
option :network, aliases: %w(-n), required: true, desc: "The name of the CloudFormation stack that contains networking settings"
|
19
|
+
option :database_url, aliases: %w(-d), desc: "The DATABASE_URL for the Rails application"
|
20
|
+
option :image_id, aliases: %w(-i), desc: "A custom AMI to use instead of the default Ruby Elastic Beanstalk AMI"
|
21
|
+
option :instance_type, aliases: %w(-t), desc: "A default instance type to use for all environments instead of c4.large"
|
22
|
+
option :keypair, aliases: %w(-k), desc: "Required on first run. The EC2 keypair to use for Elastic Beanstalk instances"
|
23
|
+
option :logging_endpoint, aliases: %w(-l), desc: "An HTTP endpoint that can receive logs from one-off commands"
|
24
|
+
option :public_key, aliases: %w(-p), desc: "For end-to-end encryption. The public key of the SSL certificate the ELB will verify to communicate with your Rails app"
|
25
|
+
option :secret_key_base, aliases: %w(-b), desc: "The SECRET_KEY_BASE for the Rails application"
|
26
|
+
option :ssl_certificate_arn, aliases: %w(-s), desc: "The ARN of the SSL server certificate stored in IAM to attach to the ELB"
|
23
27
|
def configure
|
24
28
|
@verbose = options[:verbose]
|
25
29
|
ElasticBeans::Command::Configure.new(
|
30
|
+
database_url: options[:database_url],
|
26
31
|
image_id: options[:image_id],
|
27
32
|
instance_type: options[:instance_type],
|
28
33
|
keypair: options[:keypair],
|
29
34
|
logging_endpoint: options[:logging_endpoint],
|
30
35
|
public_key: options[:public_key],
|
31
|
-
|
36
|
+
secret_key_base: options[:secret_key_base],
|
37
|
+
ssl_certificate_arn: options[:ssl_certificate_arn],
|
32
38
|
application: application(name: options[:application]),
|
33
39
|
network: network(stack_name: options[:network]),
|
34
40
|
elastic_beanstalk: elastic_beanstalk_client,
|
@@ -41,10 +47,10 @@ class ElasticBeans::CLI < Thor
|
|
41
47
|
|
42
48
|
desc ElasticBeans::Command::Create::USAGE, ElasticBeans::Command::Create::DESC
|
43
49
|
long_desc ElasticBeans::Command::Create::LONG_DESC
|
44
|
-
option :application, aliases: %w(-a), required: true
|
45
|
-
option :dns, aliases: %w(-d)
|
46
|
-
option :queue, aliases: %w(-q)
|
47
|
-
option :tags, type: :hash, default: {}
|
50
|
+
option :application, aliases: %w(-a), required: true, desc: APPLICATION_DESC
|
51
|
+
option :dns, aliases: %w(-d), desc: "A Route53 DNS entry to create or update for a new webserver environment"
|
52
|
+
option :queue, aliases: %w(-q), desc: "The name of the queue a new worker environment should listen to, e.g. `default`"
|
53
|
+
option :tags, type: :hash, default: {}, desc: "EC2 tags to apply to the new environment as a Hash, e.g. `--tags=Environment:development Team:mine`"
|
48
54
|
def create(environment_type)
|
49
55
|
@verbose = options[:verbose]
|
50
56
|
ElasticBeans::Command::Create.new(
|
@@ -64,7 +70,7 @@ class ElasticBeans::CLI < Thor
|
|
64
70
|
|
65
71
|
desc ElasticBeans::Command::Deploy::USAGE, ElasticBeans::Command::Deploy::DESC
|
66
72
|
long_desc ElasticBeans::Command::Deploy::LONG_DESC
|
67
|
-
option :application, aliases: %w(-a), required: true
|
73
|
+
option :application, aliases: %w(-a), required: true, desc: APPLICATION_DESC
|
68
74
|
def deploy
|
69
75
|
@verbose = options[:verbose]
|
70
76
|
ElasticBeans::Command::Deploy.new(
|
@@ -81,7 +87,7 @@ class ElasticBeans::CLI < Thor
|
|
81
87
|
|
82
88
|
desc ElasticBeans::Command::Exec::USAGE, ElasticBeans::Command::Exec::DESC
|
83
89
|
long_desc ElasticBeans::Command::Exec::LONG_DESC
|
84
|
-
option :application, aliases: %w(-a), required: true
|
90
|
+
option :application, aliases: %w(-a), required: true, desc: APPLICATION_DESC
|
85
91
|
def exec(*command_parts)
|
86
92
|
@verbose = options[:verbose]
|
87
93
|
ElasticBeans::Command::Exec.new(
|
@@ -97,10 +103,10 @@ class ElasticBeans::CLI < Thor
|
|
97
103
|
|
98
104
|
desc ElasticBeans::Command::Scale::USAGE, ElasticBeans::Command::Scale::DESC
|
99
105
|
long_desc ElasticBeans::Command::Scale::LONG_DESC
|
100
|
-
option :application, aliases: %w(-a), required: true
|
101
|
-
option :minimum, aliases: %w(-i --min), required: true
|
102
|
-
option :maximum, aliases: %w(-m --max), required: true
|
103
|
-
option :queue, aliases: %w(-q)
|
106
|
+
option :application, aliases: %w(-a), required: true, desc: APPLICATION_DESC
|
107
|
+
option :minimum, aliases: %w(-i --min), required: true, desc: "The minimum number of healthy instances to keep"
|
108
|
+
option :maximum, aliases: %w(-m --max), required: true, desc: "The maximum number of healthy instances to keep"
|
109
|
+
option :queue, aliases: %w(-q), desc: "The name of the queue to identify the worker environment to scale, e.g. `default`"
|
104
110
|
def scale(environment_type)
|
105
111
|
@verbose = options[:verbose]
|
106
112
|
ElasticBeans::Command::Scale.new(
|
@@ -119,7 +125,7 @@ class ElasticBeans::CLI < Thor
|
|
119
125
|
|
120
126
|
desc ElasticBeans::Command::SetEnv::USAGE, ElasticBeans::Command::SetEnv::DESC
|
121
127
|
long_desc ElasticBeans::Command::SetEnv::LONG_DESC
|
122
|
-
option :application, aliases: %w(-a), required: true
|
128
|
+
option :application, aliases: %w(-a), required: true, desc: APPLICATION_DESC
|
123
129
|
def setenv(*env_pairs)
|
124
130
|
@verbose = options[:verbose]
|
125
131
|
ElasticBeans::Command::SetEnv.new(
|
@@ -4,6 +4,9 @@ require "elastic_beans/aws/cloudformation_stack"
|
|
4
4
|
require "elastic_beans/error"
|
5
5
|
|
6
6
|
module ElasticBeans
|
7
|
+
# An Elastic Beanstalk application which should exist.
|
8
|
+
#
|
9
|
+
# If the application does not exist, an error will be raised when attempting to access it.
|
7
10
|
class Application
|
8
11
|
attr_reader :name
|
9
12
|
|
@@ -14,6 +17,10 @@ module ElasticBeans
|
|
14
17
|
@stack = ElasticBeans::Aws::CloudformationStack.new(name, cloudformation: cloudformation)
|
15
18
|
end
|
16
19
|
|
20
|
+
# Returns an ElasticBeans::ApplicationVersion reflecting the deployed version of the application from the
|
21
|
+
# latest-updated environment.
|
22
|
+
#
|
23
|
+
# Ignores terminated or terminating environments.
|
17
24
|
def deployed_version
|
18
25
|
response = elastic_beanstalk.describe_environments(application_name: name)
|
19
26
|
live_environments = response.environments.select { |environment| environment.status !~ /Terminat/ }
|
@@ -26,6 +33,9 @@ module ElasticBeans
|
|
26
33
|
retry
|
27
34
|
end
|
28
35
|
|
36
|
+
# Returns an ElasticBeans::ConfigurationTemplate for each saved configuration of the Elastic Beanstalk application.
|
37
|
+
#
|
38
|
+
# See ElasticBeans::ConfigurationTemplate::new_from_existing for details on determining appropriate types.
|
29
39
|
def configuration_templates
|
30
40
|
response = elastic_beanstalk.describe_applications(application_names: [name])
|
31
41
|
application = response.applications[0]
|
@@ -46,6 +56,7 @@ module ElasticBeans
|
|
46
56
|
retry
|
47
57
|
end
|
48
58
|
|
59
|
+
# Returns the ElasticBeans::EnvVars for this application.
|
49
60
|
def env_vars
|
50
61
|
unless exists?
|
51
62
|
raise MissingApplicationError
|
@@ -54,6 +65,9 @@ module ElasticBeans
|
|
54
65
|
EnvVars.new(application: self, s3: s3)
|
55
66
|
end
|
56
67
|
|
68
|
+
# Returns an ElasticBeans::Environment for each un-terminated environment in the Elastic Beanstalk application.
|
69
|
+
#
|
70
|
+
# See ElasticBeans::Environment::new_from_existing for details on determining appropriate types.
|
57
71
|
def environments
|
58
72
|
response = elastic_beanstalk.describe_environments(application_name: name)
|
59
73
|
live_environments = response.environments.select { |environment| environment.status !~ /Terminat/ }
|
@@ -69,6 +83,10 @@ module ElasticBeans
|
|
69
83
|
retry
|
70
84
|
end
|
71
85
|
|
86
|
+
# Returns the name of the S3 bucket in which to store artifacts for this application.
|
87
|
+
# Looks for the default Elastic Beanstalk bucket, which is of the form +elasticbeanstalk-REGION-ACCOUNT_ID+.
|
88
|
+
#
|
89
|
+
# Raises an error if the bucket cannot be found.
|
72
90
|
def bucket_name
|
73
91
|
return @bucket_name if @bucket_name
|
74
92
|
bucket = s3.list_buckets.buckets.find { |bucket| bucket.name.start_with?("elasticbeanstalk-") }
|
@@ -78,6 +96,10 @@ module ElasticBeans
|
|
78
96
|
@bucket_name = bucket.name
|
79
97
|
end
|
80
98
|
|
99
|
+
# Enqueues a one-off command to be run on the application's +exec+ environment.
|
100
|
+
# Does not wait for action to be taken, but returns immediately after enqueuing the command.
|
101
|
+
#
|
102
|
+
# Raises an error if the exec environment cannot be found.
|
81
103
|
def enqueue_command(command, sqs:)
|
82
104
|
if environments.none? { |environment| environment.is_a?(Environment::Exec) }
|
83
105
|
raise MissingExecEnvironmentError
|
@@ -93,6 +115,7 @@ module ElasticBeans
|
|
93
115
|
stack.stack_output("ExecQueueUrl")
|
94
116
|
end
|
95
117
|
|
118
|
+
# Returns an ElasticBeans::ApplicationVersion for each version of the Elastic Beanstalk application.
|
96
119
|
def versions
|
97
120
|
response = elastic_beanstalk.describe_application_versions(application_name: name)
|
98
121
|
response.application_versions.map { |version| ElasticBeans::ApplicationVersion.new(version.version_label) }
|
@@ -101,12 +124,15 @@ module ElasticBeans
|
|
101
124
|
retry
|
102
125
|
end
|
103
126
|
|
127
|
+
# Returns the +Worker{QUEUE}QueueUrl+ from the application CloudFormation stack.
|
104
128
|
def worker_queue_url(queue)
|
105
129
|
queue_attribute = queue.to_s.downcase
|
106
130
|
queue_attribute[0] = queue_attribute[0].upcase
|
107
131
|
stack.stack_output("Worker#{queue_attribute}QueueUrl")
|
108
132
|
end
|
109
133
|
|
134
|
+
# Returns the name of each queue discovered in the application CloudFormation stack.
|
135
|
+
# Looks for outputs of the form +Worker{QUEUE}QueueUrl+.
|
110
136
|
def worker_queues
|
111
137
|
stack.stack_outputs.each_with_object([]) do |(output_key, _), acc|
|
112
138
|
match = /\AWorker(?<queue>\w+)QueueUrl\z/.match(output_key)
|
@@ -133,12 +159,16 @@ module ElasticBeans
|
|
133
159
|
retry
|
134
160
|
end
|
135
161
|
|
162
|
+
# :nodoc: all
|
163
|
+
# @!visibility private
|
136
164
|
class MissingApplicationError < ElasticBeans::Error
|
137
165
|
def message
|
138
166
|
"Application `#{@application_name}' does not exist. Please create the Elastic Beanstalk application using a CloudFormation stack."
|
139
167
|
end
|
140
168
|
end
|
141
169
|
|
170
|
+
# :nodoc: all
|
171
|
+
# @!visibility private
|
142
172
|
class MissingBucketError < ElasticBeans::Error
|
143
173
|
def message
|
144
174
|
"Cannot find the Elastic Beanstalk S3 bucket." \
|
@@ -146,12 +176,14 @@ module ElasticBeans
|
|
146
176
|
end
|
147
177
|
end
|
148
178
|
|
179
|
+
# :nodoc: all
|
180
|
+
# @!visibility private
|
149
181
|
class MissingExecEnvironmentError < ElasticBeans::Error
|
150
182
|
def message
|
151
183
|
<<-MESSAGE
|
152
184
|
A one-off command cannot be executed because the "exec" environment does not exist. Please create it:
|
153
185
|
|
154
|
-
#{
|
186
|
+
#{command_as_string "-a APPLICATION exec"}
|
155
187
|
MESSAGE
|
156
188
|
end
|
157
189
|
end
|
@@ -9,11 +9,18 @@ module ElasticBeans
|
|
9
9
|
class ApplicationVersion
|
10
10
|
attr_reader :version_label
|
11
11
|
|
12
|
+
# Create a representation of an existing application version in Elastic Beanstalk.
|
12
13
|
def initialize(version_label)
|
13
14
|
@version_label = version_label
|
14
15
|
end
|
15
16
|
|
16
17
|
class << self
|
18
|
+
# Create a new Elastic Beanstalk application version from the HEAD git commit of the given +working_directory+.
|
19
|
+
# Returns immediately if the application version already exists in Elastic Beanstalk.
|
20
|
+
#
|
21
|
+
# Injects helper code from this library to enable the functionality described in the README.
|
22
|
+
#
|
23
|
+
# Raises an error if the application version cannot be processed by Elastic Beanstalk.
|
17
24
|
def create(working_directory:, application:, elastic_beanstalk:, s3:)
|
18
25
|
version_label = fetch_version_label(working_directory)
|
19
26
|
if application.versions.any? { |version| version.version_label == version_label }
|
@@ -21,7 +28,7 @@ module ElasticBeans
|
|
21
28
|
end
|
22
29
|
|
23
30
|
archive_path = File.join(working_directory, ".elasticbeanstalk", "#{version_label}.zip")
|
24
|
-
create_archive(sha: version_label, path: archive_path)
|
31
|
+
create_archive(working_directory: working_directory, sha: version_label, path: archive_path)
|
25
32
|
create_application_version(
|
26
33
|
path: archive_path,
|
27
34
|
version_label: version_label,
|
@@ -68,9 +75,11 @@ module ElasticBeans
|
|
68
75
|
raise FailedApplicationVersion.new(version_label: version_label, status: status)
|
69
76
|
end
|
70
77
|
|
71
|
-
def create_archive(sha:, path:)
|
78
|
+
def create_archive(working_directory:, sha:, path:)
|
72
79
|
FileUtils.mkdir_p(File.dirname(path))
|
73
|
-
|
80
|
+
Dir.chdir(working_directory) do
|
81
|
+
system("git archive --format=zip -o #{path} #{sha}")
|
82
|
+
end
|
74
83
|
unless $?.success?
|
75
84
|
raise FailedArchiveError.new(sha: sha, path: path)
|
76
85
|
end
|
@@ -157,6 +166,8 @@ module ElasticBeans
|
|
157
166
|
|
158
167
|
private
|
159
168
|
|
169
|
+
# :nodoc: all
|
170
|
+
# @!visibility private
|
160
171
|
class FailedApplicationVersion < ElasticBeans::Error
|
161
172
|
include Nesty::NestedError
|
162
173
|
|
@@ -174,6 +185,8 @@ module ElasticBeans
|
|
174
185
|
end
|
175
186
|
end
|
176
187
|
|
188
|
+
# :nodoc: all
|
189
|
+
# @!visibility private
|
177
190
|
class FailedArchiveError < ElasticBeans::Error
|
178
191
|
def initialize(sha:, path:)
|
179
192
|
@sha = sha
|
@@ -185,6 +198,8 @@ module ElasticBeans
|
|
185
198
|
end
|
186
199
|
end
|
187
200
|
|
201
|
+
# :nodoc: all
|
202
|
+
# @!visibility private
|
188
203
|
class InvalidVersionWorkingDirectoryError < ElasticBeans::Error
|
189
204
|
def initialize(wd:)
|
190
205
|
@wd = wd
|
@@ -3,41 +3,41 @@ require "elastic_beans/error/environments_not_ready"
|
|
3
3
|
|
4
4
|
module ElasticBeans
|
5
5
|
module Command
|
6
|
+
# :nodoc: all
|
6
7
|
class Configure
|
7
|
-
USAGE = "configure
|
8
|
+
USAGE = "configure"
|
8
9
|
DESC = "Configure the given Elastic Beanstalk application"
|
9
10
|
LONG_DESC = <<-LONG_DESC
|
10
11
|
Configure the given Elastic Beanstalk application.
|
11
12
|
Creates and updates configuration templates for each possible environment type.
|
12
13
|
Updates running environments with the new configuration.
|
13
14
|
|
14
|
-
Requires the networking CloudFormation stack and application name.
|
15
15
|
Requires AWS credentials to be set in the environment, i.e. AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY.
|
16
|
-
Some environment variables that Rails always requires must be set:
|
17
|
-
|
18
|
-
* DATABASE_URL
|
19
|
-
* SECRET_KEY_BASE
|
20
16
|
LONG_DESC
|
21
17
|
|
22
18
|
def initialize(
|
19
|
+
database_url:,
|
23
20
|
image_id:,
|
24
21
|
instance_type:,
|
25
22
|
keypair:,
|
26
23
|
logging_endpoint:,
|
27
24
|
public_key:,
|
28
|
-
|
25
|
+
secret_key_base:,
|
26
|
+
ssl_certificate_arn:,
|
29
27
|
application:,
|
30
28
|
network:,
|
31
29
|
elastic_beanstalk:,
|
32
30
|
iam:,
|
33
31
|
ui:
|
34
32
|
)
|
33
|
+
@database_url = database_url
|
35
34
|
@image_id = image_id
|
36
35
|
@instance_type = instance_type
|
37
36
|
@keypair = keypair
|
38
37
|
@logging_endpoint = logging_endpoint
|
39
38
|
@public_key = public_key
|
40
|
-
@
|
39
|
+
@secret_key_base = secret_key_base
|
40
|
+
@ssl_certificate_arn = ssl_certificate_arn
|
41
41
|
@application = application
|
42
42
|
@network = network
|
43
43
|
@elastic_beanstalk = elastic_beanstalk
|
@@ -83,7 +83,7 @@ Some environment variables that Rails always requires must be set:
|
|
83
83
|
keypair: keypair,
|
84
84
|
iam: iam,
|
85
85
|
public_key: public_key,
|
86
|
-
|
86
|
+
ssl_certificate_arn: ssl_certificate_arn,
|
87
87
|
)
|
88
88
|
progressbar.increment
|
89
89
|
progressbar.log("Updating exec configuration template...")
|
@@ -161,25 +161,19 @@ Some environment variables that Rails always requires must be set:
|
|
161
161
|
|
162
162
|
attr_reader(
|
163
163
|
:application,
|
164
|
+
:database_url,
|
164
165
|
:image_id,
|
165
166
|
:instance_type,
|
166
167
|
:keypair,
|
167
168
|
:logging_endpoint,
|
168
169
|
:network,
|
169
170
|
:public_key,
|
170
|
-
:
|
171
|
+
:secret_key_base,
|
172
|
+
:ssl_certificate_arn,
|
171
173
|
:elastic_beanstalk,
|
172
174
|
:iam,
|
173
175
|
:ui,
|
174
176
|
)
|
175
|
-
|
176
|
-
def database_url
|
177
|
-
ENV['DATABASE_URL']
|
178
|
-
end
|
179
|
-
|
180
|
-
def secret_key_base
|
181
|
-
ENV['SECRET_KEY_BASE']
|
182
|
-
end
|
183
177
|
end
|
184
178
|
end
|
185
179
|
end
|
@@ -6,14 +6,17 @@ require "elastic_beans/environment/webserver"
|
|
6
6
|
|
7
7
|
module ElasticBeans
|
8
8
|
module Command
|
9
|
+
# :nodoc: all
|
9
10
|
class Create
|
10
|
-
USAGE = "create
|
11
|
+
USAGE = "create ENVIRONMENT_TYPE"
|
11
12
|
DESC = "Create a new environment in Elastic Beanstalk and attach it to relevant resources"
|
12
13
|
LONG_DESC = <<-LONG_DESC
|
13
|
-
Create a new environment in Elastic Beanstalk and attach it to relevant resources
|
14
|
-
The environment type must be one of the recognized types: webserver, worker, or scheduled.
|
14
|
+
Create a new environment in Elastic Beanstalk and attach it to relevant resources.
|
15
|
+
The environment type must be one of the recognized types: webserver, worker, exec, or scheduled.
|
16
|
+
|
17
|
+
A new environment will use the same deployed version as all other environments.
|
18
|
+
If this is the first environment, a new version will be created from the HEAD git commit of the working directory.
|
15
19
|
|
16
|
-
Requires the Elastic Beanstalk application name.
|
17
20
|
Requires AWS credentials to be set in the environment, i.e. AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY.
|
18
21
|
LONG_DESC
|
19
22
|
|
@@ -133,6 +136,7 @@ Requires AWS credentials to be set in the environment, i.e. AWS_ACCESS_KEY_ID an
|
|
133
136
|
raise UnhealthyEnvironmentError.new(environment_name: environment.name)
|
134
137
|
end
|
135
138
|
|
139
|
+
# :nodoc: all
|
136
140
|
class UnhealthyEnvironmentError < ElasticBeans::Error
|
137
141
|
def initialize(environment_name:)
|
138
142
|
@environment_name = environment_name
|
@@ -3,13 +3,13 @@ require "elastic_beans/error/environments_not_ready"
|
|
3
3
|
|
4
4
|
module ElasticBeans
|
5
5
|
module Command
|
6
|
+
# :nodoc: all
|
6
7
|
class Deploy
|
7
|
-
USAGE = "deploy
|
8
|
+
USAGE = "deploy"
|
8
9
|
DESC = "Deploy the HEAD git commit to all environments in Elastic Beanstalk"
|
9
10
|
LONG_DESC = <<-LONG_DESC
|
10
|
-
Deploy the HEAD git commit to all environments in Elastic Beanstalk.
|
11
|
+
Deploy the HEAD git commit of the working directory to all environments in Elastic Beanstalk.
|
11
12
|
|
12
|
-
Requires the application name.
|
13
13
|
Requires AWS credentials to be set in the environment, i.e. AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY.
|
14
14
|
LONG_DESC
|
15
15
|
|
@@ -2,15 +2,16 @@ require "shellwords"
|
|
2
2
|
|
3
3
|
module ElasticBeans
|
4
4
|
module Command
|
5
|
+
# :nodoc: all
|
5
6
|
class Exec
|
6
|
-
USAGE = "exec
|
7
|
+
USAGE = "exec COMMAND STRING"
|
7
8
|
DESC = "Run an arbitrary command in the context of your application"
|
8
9
|
LONG_DESC = <<-LONG_DESC
|
9
10
|
Run an arbitrary command in the context of your application.
|
10
11
|
The command is run in an "exec" environment, separate from your webserver or worker environments.
|
11
|
-
|
12
|
+
You must create the exec environment prior to this command being run: `beans create exec -a APPLICATION`.
|
13
|
+
Upload output from the command to an HTTP endpoint.
|
12
14
|
|
13
|
-
Requires the Elastic Beanstalk application name.
|
14
15
|
Requires AWS credentials to be set in the environment, i.e. AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY.
|
15
16
|
LONG_DESC
|
16
17
|
|
@@ -3,13 +3,13 @@ require "elastic_beans/error/environments_not_ready"
|
|
3
3
|
|
4
4
|
module ElasticBeans
|
5
5
|
module Command
|
6
|
+
# :nodoc: all
|
6
7
|
class Scale
|
7
|
-
USAGE = "scale
|
8
|
+
USAGE = "scale ENVIRONMENT_TYPE"
|
8
9
|
DESC = "Change the autoscaling minimum and maximum for the given environment"
|
9
10
|
LONG_DESC = <<-LONG_DESC
|
10
11
|
Change the autoscaling minimum and maximum for the given environment.
|
11
12
|
|
12
|
-
Requires the application name.
|
13
13
|
Requires AWS credentials to be set in the environment, i.e. AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY.
|
14
14
|
LONG_DESC
|
15
15
|
|