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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +91 -16
  3. data/exe/beans +26 -20
  4. data/lib/elastic_beans/application.rb +33 -1
  5. data/lib/elastic_beans/application_version.rb +18 -3
  6. data/lib/elastic_beans/aws/cloudformation_stack.rb +2 -0
  7. data/lib/elastic_beans/command/configure.rb +12 -18
  8. data/lib/elastic_beans/command/create.rb +8 -4
  9. data/lib/elastic_beans/command/deploy.rb +3 -3
  10. data/lib/elastic_beans/command/exec.rb +4 -3
  11. data/lib/elastic_beans/command/scale.rb +2 -2
  12. data/lib/elastic_beans/command/set_env.rb +4 -3
  13. data/lib/elastic_beans/command/talk.rb +1 -0
  14. data/lib/elastic_beans/command/version.rb +1 -0
  15. data/lib/elastic_beans/command.rb +2 -0
  16. data/lib/elastic_beans/configuration_template/base.rb +12 -2
  17. data/lib/elastic_beans/configuration_template/exec.rb +6 -0
  18. data/lib/elastic_beans/configuration_template/scheduler.rb +4 -0
  19. data/lib/elastic_beans/configuration_template/webserver.rb +11 -4
  20. data/lib/elastic_beans/configuration_template/worker.rb +6 -0
  21. data/lib/elastic_beans/configuration_template.rb +32 -11
  22. data/lib/elastic_beans/dns_entry.rb +2 -0
  23. data/lib/elastic_beans/env_vars.rb +6 -0
  24. data/lib/elastic_beans/environment/exec.rb +13 -4
  25. data/lib/elastic_beans/environment/scheduler.rb +13 -4
  26. data/lib/elastic_beans/environment/webserver.rb +13 -4
  27. data/lib/elastic_beans/environment/worker.rb +15 -4
  28. data/lib/elastic_beans/environment.rb +74 -18
  29. data/lib/elastic_beans/error/environments_not_ready.rb +2 -0
  30. data/lib/elastic_beans/error.rb +1 -0
  31. data/lib/elastic_beans/exec/init.rb +2 -0
  32. data/lib/elastic_beans/exec/sqs_consumer.rb +2 -0
  33. data/lib/elastic_beans/network.rb +1 -0
  34. data/lib/elastic_beans/rack/exec.rb +5 -0
  35. data/lib/elastic_beans/ui.rb +2 -0
  36. data/lib/elastic_beans/version.rb +1 -1
  37. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cb92ef373018a6f507082e86909372817ddd7042
4
- data.tar.gz: 49eb178cbdad8e93930afa6998577588093d7994
3
+ metadata.gz: 2b231170798c366604081d7d0800c0c332a386d6
4
+ data.tar.gz: 661c9aca9b2ae4fdc6402cd02c5b3d37c4f62d76
5
5
  SHA512:
6
- metadata.gz: e8b5a831464781d60d79e18c66c723a361d02c597b1e5cea19ed059e62ab70ce8be4f1a80fa4154d37455b7d4e455e409b833d50ccf198e77c600ca2a7148db9
7
- data.tar.gz: eb62d218f9ab435ff4d90beb9e8f209ddedcf289e981cd09302bc4390763a2a7b1c90d9d0acab694f3deeeefc64c6ad15fa98f1eab0f33fc7ea7c305f8d8baed
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
- # Rails will not start without DATABASE_URL and SECRET_TOKEN_BASE
23
- export DATABASE_URL=mysql2://... SECRET_KEY_BASE=abc123...
24
- beans configure -n myapp-networking -a myapp -k KEYPAIR -s SSL_CERTIFICATE_ID [-i IMAGE_ID] [-t INSTANCE_TYPE] [-l LOGGING_HTTP_ENDPOINT]
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
- # Deploy the HEAD git commit to all environments
45
- # Creates an application version named after the git ref
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 [-k KEYPAIR] [-s SSL_CERTIFICATE_ID] [-i IMAGE_ID] [-t INSTANCE_TYPE]
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
- app = ElasticBeans::Application.new(
57
- name: "myapp",
58
- cloudformation: Aws::CloudFormation::Client.new,
59
- elastic_beanstalk: Aws::ElasticBeanstalk::Client.new,
60
- s3: Aws::S3::Client.new,
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
- The `Default` name must be present.
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 :image_id, aliases: %w(-i)
18
- option :instance_type, aliases: %w(-t)
19
- option :keypair, aliases: %w(-k)
20
- option :logging_endpoint, aliases: %w(-l)
21
- option :public_key, aliases: %w(-p)
22
- option :ssl_certificate_id, aliases: %w(-s)
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
- ssl_certificate_id: options[:ssl_certificate_id],
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
- #{$0} create -a APPLICATION exec
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
- system("git archive --format=zip -o #{path} #{sha}")
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
@@ -2,6 +2,8 @@ require "aws-sdk"
2
2
  require "elastic_beans/error"
3
3
 
4
4
  module ElasticBeans
5
+ # :nodoc: all
6
+ # @!visibility private
5
7
  module Aws
6
8
  class CloudformationStack
7
9
  STACK_STATUSES = %w(CREATE_COMPLETE UPDATE_COMPLETE UPDATE_ROLLBACK_COMPLETE)
@@ -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 -n NETWORK -a APPLICATION [-k KEYPAIR] [-p PUBLIC_KEY] [-s SSL_CERTIFICATE_ID] [-i IMAGE_ID] [-t INSTANCE_TYPE] [-l EXEC_LOGGING_HTTP_ENDPOINT]"
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
- ssl_certificate_id:,
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
- @ssl_certificate_id = ssl_certificate_id
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
- ssl_certificate_id: ssl_certificate_id,
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
- :ssl_certificate_id,
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 -a APPLICATION [-q QUEUE] [-d PRETTY_DNS] ENVIRONMENT_TYPE"
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 -a APPLICATION"
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 -a APPLICATION rake db:migrate"
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
- Upload gzipped output from the command to an HTTP endpoint.
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 -a APPLICATION -i MINIMUM -m MAXIMUM ENVIRONMENT"
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