elastic_beans 0.2.0 → 0.3.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.
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
@@ -3,14 +3,15 @@ require "elastic_beans/error/environments_not_ready"
3
3
 
4
4
  module ElasticBeans
5
5
  module Command
6
+ # :nodoc: all
6
7
  class SetEnv
7
- USAGE = "setenv -a APPLICATION KEY=value [KEY=value]..."
8
+ USAGE = "setenv KEY=[value] [KEY=[value]]..."
8
9
  DESC = "Update environment variables in the Elastic Beanstalk application"
9
10
  LONG_DESC = <<-LONG_DESC
10
11
  Update environment variables in the Elastic Beanstalk application.
11
- Updates all running environments and configuration templates resulting from `configure`.
12
+ These are stored in S3 to avoid the 4096-byte limit on environment variables in Elastic Beanstalk.
13
+ Restarts all running environments so they load the updated environment.
12
14
 
13
- Requires the 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
 
@@ -1,5 +1,6 @@
1
1
  module ElasticBeans
2
2
  module Command
3
+ # :nodoc: all
3
4
  class Talk
4
5
  BEANSTALK = <<-BEANSTALK
5
6
  ...........................,:~~:............................
@@ -1,5 +1,6 @@
1
1
  module ElasticBeans
2
2
  module Command
3
+ # :nodoc: all
3
4
  class Version
4
5
  def initialize(ui:)
5
6
  @ui = ui
@@ -8,6 +8,8 @@ require "elastic_beans/command/talk"
8
8
  require "elastic_beans/command/version"
9
9
 
10
10
  module ElasticBeans
11
+ # :nodoc: all
12
+ # @!visibility private
11
13
  module Command
12
14
  end
13
15
  end
@@ -2,13 +2,19 @@ require "elastic_beans/error"
2
2
 
3
3
  module ElasticBeans
4
4
  class ConfigurationTemplate
5
+ # The "base" configuration template stored in the Elastic Beanstalk application.
6
+ # Common settings are stored here, such as networking settings and environment variables.
7
+ # Used as a "source configuration" when creating other configuration templates, so that new templates share the
8
+ # existing configuration.
5
9
  class Base < ElasticBeans::ConfigurationTemplate
10
+ # The solution stack used for a new application. Should not be hardcoded, but here we are.
6
11
  SOLUTION_STACK_NAME = "64bit Amazon Linux 2016.09 v2.2.0 running Ruby 2.3 (Puma)"
7
12
 
8
13
  def initialize(**args)
9
14
  super(name: "base", **args)
10
15
  end
11
16
 
17
+ # Overrides ElasticBeans::ConfigurationTemplate#upsert to add the solution stack to the base configuration.
12
18
  def upsert(**args)
13
19
  @option_settings = build_option_settings(**args)
14
20
  if configuration_settings_description
@@ -32,16 +38,18 @@ module ElasticBeans
32
38
 
33
39
  protected
34
40
 
41
+ # Constructs the common configuration for all environments.
42
+ # +network+, +database_url+, +secret_key_base+, +keypair+, and +iam+ are all required on first run.
35
43
  def build_option_settings(
36
44
  network: nil,
37
45
  database_url: nil,
38
46
  secret_key_base: nil,
47
+ keypair: nil,
48
+ iam: nil,
39
49
  image_id: nil,
40
50
  instance_type: nil,
41
- keypair: nil,
42
51
  min_size: nil,
43
52
  max_size: nil,
44
- iam: nil,
45
53
  **_
46
54
  )
47
55
  instance_profile_setting = template_option_setting(namespace: "aws:autoscaling:launchconfiguration", option_name: "IamInstanceProfile", override: instance_profile(iam))
@@ -126,6 +134,8 @@ module ElasticBeans
126
134
  network.vpc if network
127
135
  end
128
136
 
137
+ # :nodoc: all
138
+ # @!visibility private
129
139
  class MissingInstanceProfileError < ElasticBeans::Error
130
140
  def message
131
141
  "Could not find Elastic Beanstalk instance profile." \
@@ -3,6 +3,8 @@ require "elastic_beans/error"
3
3
 
4
4
  module ElasticBeans
5
5
  class ConfigurationTemplate
6
+ # The "exec" configuration template stored in the Elastic Beanstalk application.
7
+ # Settings for the exec environment are stored here, such as the exec queue URL and the logging endpoint.
6
8
  class Exec < ElasticBeans::ConfigurationTemplate::Base
7
9
  def initialize(**args)
8
10
  super(name: "exec", **args)
@@ -10,6 +12,8 @@ module ElasticBeans
10
12
 
11
13
  protected
12
14
 
15
+ # Constructs the configuration for the exec environment.
16
+ # +logging_endpoint+, if provided, must be a valid HTTPS URL.
13
17
  def build_option_settings(logging_endpoint: nil, **_)
14
18
  if logging_endpoint
15
19
  begin
@@ -36,6 +40,8 @@ module ElasticBeans
36
40
  super + settings
37
41
  end
38
42
 
43
+ # :nodoc: all
44
+ # @!visibility private
39
45
  class InvalidLoggingEndpointError < ElasticBeans::Error
40
46
  def initialize(logging_endpoint:)
41
47
  @logging_endpoint = logging_endpoint
@@ -1,5 +1,7 @@
1
1
  module ElasticBeans
2
2
  class ConfigurationTemplate
3
+ # The "scheduler" configuration template stored in the Elastic Beanstalk application.
4
+ # Settings for the scheduler environment are stored here.
3
5
  class Scheduler < ElasticBeans::ConfigurationTemplate::Base
4
6
  def initialize(**args)
5
7
  super(name: "scheduler", **args)
@@ -7,6 +9,8 @@ module ElasticBeans
7
9
 
8
10
  protected
9
11
 
12
+ # Constructs the configuration for the scheduler environment.
13
+ # No special options here!
10
14
  def build_option_settings(**_)
11
15
  super + [
12
16
  template_option_setting(namespace: "aws:elasticbeanstalk:application", option_name: "Application Healthcheck URL", default: "HTTP:80/"),
@@ -2,6 +2,8 @@ require "elastic_beans/error"
2
2
 
3
3
  module ElasticBeans
4
4
  class ConfigurationTemplate
5
+ # The "webserver" configuration template stored in the Elastic Beanstalk application.
6
+ # Settings for the webserver environment are stored here.
5
7
  class Webserver < ElasticBeans::ConfigurationTemplate::Base
6
8
  def initialize(**args)
7
9
  super(name: "webserver", **args)
@@ -9,10 +11,12 @@ module ElasticBeans
9
11
 
10
12
  protected
11
13
 
12
- def build_option_settings(network: nil, public_key: nil, ssl_certificate_id: nil, **_)
14
+ # Constructs the configuration for the webserver environment.
15
+ # All arguments are required on first run.
16
+ def build_option_settings(network: nil, public_key: nil, ssl_certificate_arn: nil, **_)
13
17
  public_key_policy_names_setting = template_option_setting(namespace: "aws:elb:policies:backendencryption", option_name: "PublicKeyPolicyNames", default: "backendkey")
14
18
  public_key_setting = template_option_setting(namespace: "aws:elb:policies:#{public_key_policy_names_setting[:value]}", option_name: "PublicKey", override: public_key)
15
- ssl_certificate_setting = template_option_setting(namespace: "aws:elb:listener:443", option_name: "SSLCertificateId", override: ssl_certificate_id)
19
+ ssl_certificate_setting = template_option_setting(namespace: "aws:elb:listener:443", option_name: "SSLCertificateId", override: ssl_certificate_arn)
16
20
  if public_key_setting[:value].nil? || ssl_certificate_setting[:value].nil?
17
21
  raise NoEncryptionSettingsError
18
22
  end
@@ -42,13 +46,16 @@ module ElasticBeans
42
46
  network.elb_security_groups[0] if network
43
47
  end
44
48
 
49
+ # :nodoc: all
50
+ # @!visibility private
45
51
  class NoEncryptionSettingsError < ElasticBeans::Error
46
52
  def message
47
53
  require "elastic_beans/command/configure"
48
54
  <<-MESSAGE
49
- Missing required end-to-end encryption settings. Make sure to specify SSL certificate ID and public key.
55
+ Missing required end-to-end encryption settings.
56
+ Please re-run `#{command_as_string "configure"}` and make sure to specify SSL certificate ARN and internal public key.
50
57
 
51
- #{$0} #{ElasticBeans::Command::Configure::USAGE}
58
+ #{`#{$0} help configure`}
52
59
  MESSAGE
53
60
  end
54
61
  end
@@ -1,5 +1,8 @@
1
1
  module ElasticBeans
2
2
  class ConfigurationTemplate
3
+ # The worker configuration templates stored in the Elastic Beanstalk application.
4
+ # Settings for the worker environments are stored here.
5
+ # This class is used to construct multiple worker configuration templates, one per queue.
3
6
  class Worker < ElasticBeans::ConfigurationTemplate::Base
4
7
  attr_reader :queue
5
8
 
@@ -10,6 +13,9 @@ module ElasticBeans
10
13
 
11
14
  protected
12
15
 
16
+ # Constructs the configuration for the worker environments.
17
+ # No special arguments, the +queue+ name is stored from the initializer and is used to look up the appropriate
18
+ # URL.
13
19
  def build_option_settings(**_)
14
20
  super + [
15
21
  template_option_setting(namespace: "aws:elasticbeanstalk:application", option_name: "Application Healthcheck URL", default: "HTTP:80/"),
@@ -5,11 +5,20 @@ require "elastic_beans/configuration_template/webserver"
5
5
  require "elastic_beans/configuration_template/worker"
6
6
 
7
7
  module ElasticBeans
8
+ # An Elastic Beanstalk saved configuration (also known as a configuration template) which may or may not exist.
9
+ #
10
+ # This is an abstract class; use the provided factories in this class or use a subclass (contained within this
11
+ # namespace) directly.
8
12
  class ConfigurationTemplate
13
+ # :category: Internal
9
14
  WORKER_TEMPLATE_NAME_PATTERN = /\Aworker-(?<queue>\w+)\z/
10
15
 
11
16
  attr_reader :name
12
17
 
18
+ # Create a new configuration template of a particular +type+.
19
+ # See the particular subclass being created for details on arguments.
20
+ #
21
+ # Raises an error if the +type+ is not recognized.
13
22
  def self.new_by_type(type, **args)
14
23
  case type
15
24
  when "base"
@@ -27,6 +36,10 @@ module ElasticBeans
27
36
  end
28
37
  end
29
38
 
39
+ # Create a representation of an existing configuration template.
40
+ # Uses the +template_name+ to discover the appropriate type (and queue, in the case of a worker).
41
+ #
42
+ # Raises an error if the +template_name+ is not recognized.
30
43
  def self.new_from_existing(template_name, **args)
31
44
  case template_name
32
45
  when "base"
@@ -45,6 +58,7 @@ module ElasticBeans
45
58
  end
46
59
  end
47
60
 
61
+ # :category: Internal
48
62
  def initialize(
49
63
  name:,
50
64
  application:,
@@ -56,6 +70,7 @@ module ElasticBeans
56
70
  @elastic_beanstalk = elastic_beanstalk
57
71
  end
58
72
 
73
+ # :category: Internal
59
74
  def option_settings
60
75
  # do not fetch option settings from Elastic Beanstalk, because those cannot be naively used.
61
76
  # the set built in #build_option_settings is what we care about.
@@ -63,6 +78,9 @@ module ElasticBeans
63
78
  raise MissingConfigurationError
64
79
  end
65
80
 
81
+ # Create or update the configuration template in Elastic Beanstalk.
82
+ # Arguments are passed to #build_option_settings.
83
+ # See the appropriate subclass for what the arguments should be.
66
84
  def upsert(**args)
67
85
  @option_settings = build_option_settings(**args)
68
86
  if configuration_settings_description
@@ -86,7 +104,10 @@ module ElasticBeans
86
104
 
87
105
  protected
88
106
 
89
- attr_reader :application, :elastic_beanstalk
107
+ # :category: Internal
108
+ attr_reader :application
109
+ # :category: Internal
110
+ attr_reader :elastic_beanstalk
90
111
 
91
112
  def build_option_settings(**_)
92
113
  []
@@ -123,6 +144,8 @@ module ElasticBeans
123
144
  option_setting.merge!(value: setting[:value])
124
145
  end
125
146
 
147
+ # :nodoc: all
148
+ # @!visibility private
126
149
  class InvalidConfigurationError < ElasticBeans::Error
127
150
  include Nesty::NestedError
128
151
 
@@ -139,6 +162,8 @@ module ElasticBeans
139
162
  end
140
163
  end
141
164
 
165
+ # :nodoc: all
166
+ # @!visibility private
142
167
  class MissingConfigurationError < ElasticBeans::Error
143
168
  include Nesty::NestedError
144
169
 
@@ -149,23 +174,19 @@ module ElasticBeans
149
174
  def message
150
175
  require "elastic_beans/command/configure"
151
176
  msg = <<-MESSAGE
152
- Some configuration must be set before creating an environment:
153
-
154
- * keypair
155
- * SSL configuration
156
- * environment variables that Rails always requires
157
- * DATABASE_URL
158
- * SECRET_KEY_BASE
159
-
160
- #{$0} #{ElasticBeans::Command::Configure::USAGE}
177
+ Some configuration is missing and must be set before creating or updating an environment.
161
178
  MESSAGE
162
179
  if nested
163
- msg = "The error from AWS was \"#{nested.message}\"\n#{msg}"
180
+ msg << "The error from AWS was \"#{nested.message}\"\n#{msg}\n"
181
+ else
182
+ msg << "Please re-run `#{command_as_string "configure"}`.\n\n#{`#{$0} help configure`}"
164
183
  end
165
184
  msg
166
185
  end
167
186
  end
168
187
 
188
+ # :nodoc: all
189
+ # @!visibility private
169
190
  class UnknownConfigurationType < ElasticBeans::Error
170
191
  def initialize(type:)
171
192
  @type = type
@@ -2,6 +2,8 @@ require "timeout"
2
2
  require "elastic_beans/error"
3
3
 
4
4
  module ElasticBeans
5
+ # :nodoc: all
6
+ # @!visibility private
5
7
  class DnsEntry
6
8
  DNS_NAME_PATTERN = /\A[^.]+\.[^.]+\.[^.]+\z/
7
9
 
@@ -3,6 +3,9 @@ require "aws-sdk"
3
3
  require "elastic_beans/error"
4
4
 
5
5
  module ElasticBeans
6
+ # Interfaces with environment variable storage for the Elastic Beanstalk application.
7
+ # Environment variables are stored in JSON in S3 to avoid the 4096-byte limit on environment variables in Elastic
8
+ # Beanstalk. An ebextension is used to inject these into the environment variables on the running instance.
6
9
  class EnvVars
7
10
  def initialize(application:, s3:)
8
11
  @application = application
@@ -13,6 +16,7 @@ module ElasticBeans
13
16
  @s3_key ||= "#{application.name}/env_vars.json"
14
17
  end
15
18
 
19
+ # Updates the environment variables stored in S3 by merging it with the given +env_hash+.
16
20
  def update(env_hash)
17
21
  body = env_script(existing_env_hash.merge(env_hash))
18
22
  s3.put_object(bucket: application.bucket_name, key: s3_key, body: body)
@@ -37,6 +41,8 @@ module ElasticBeans
37
41
  raise CannotAccessConfigError.new(bucket_name: application.bucket_name, key: s3_key)
38
42
  end
39
43
 
44
+ # :nodoc: all
45
+ # @!visibility private
40
46
  class CannotAccessConfigError < ElasticBeans::Error
41
47
  def initialize(bucket_name:, key:)
42
48
  @bucket_name = bucket_name
@@ -1,14 +1,23 @@
1
1
  module ElasticBeans
2
2
  class Environment
3
+ # The "exec" environment for executing one-off commands.
3
4
  class Exec < ElasticBeans::Environment
4
- protected
5
-
6
- TEMPLATE_NAME = "exec"
5
+ # :category: Internal
6
+ TYPE = "exec"
7
+ # :category: Internal
7
8
  TIER_NAME = "Worker"
9
+ # :category: Internal
8
10
  TIER_TYPE = "SQS/HTTP"
9
11
 
12
+ # Returns "exec"
13
+ def type
14
+ TYPE
15
+ end
16
+
17
+ protected
18
+
10
19
  def template_name
11
- TEMPLATE_NAME
20
+ type
12
21
  end
13
22
 
14
23
  def tier_name
@@ -1,14 +1,23 @@
1
1
  module ElasticBeans
2
2
  class Environment
3
+ # The "scheduler" environment for processing periodic tasks.
3
4
  class Scheduler < ElasticBeans::Environment
4
- protected
5
-
6
- TEMPLATE_NAME = "scheduler"
5
+ # :category: Internal
6
+ TYPE = "scheduler"
7
+ # :category: Internal
7
8
  TIER_NAME = "Worker"
9
+ # :category: Internal
8
10
  TIER_TYPE = "SQS/HTTP"
9
11
 
12
+ # Returns "scheduler"
13
+ def type
14
+ TYPE
15
+ end
16
+
17
+ protected
18
+
10
19
  def template_name
11
- TEMPLATE_NAME
20
+ type
12
21
  end
13
22
 
14
23
  def tier_name
@@ -1,14 +1,23 @@
1
1
  module ElasticBeans
2
2
  class Environment
3
+ # The "webserver" environment for responding to web requests.
3
4
  class Webserver < ElasticBeans::Environment
4
- protected
5
-
6
- TEMPLATE_NAME = "webserver"
5
+ # :category: Internal
6
+ TYPE = "webserver"
7
+ # :category: Internal
7
8
  TIER_NAME = "Webserver"
9
+ # :category: Internal
8
10
  TIER_TYPE = "Standard"
9
11
 
12
+ # Returns "webserver"
13
+ def type
14
+ TYPE
15
+ end
16
+
17
+ protected
18
+
10
19
  def template_name
11
- TEMPLATE_NAME
20
+ type
12
21
  end
13
22
 
14
23
  def tier_name
@@ -1,6 +1,15 @@
1
1
  module ElasticBeans
2
2
  class Environment
3
+ # The worker environments for background job execution.
4
+ # This class is used to construct multiple worker environments, one per queue.
3
5
  class Worker < ElasticBeans::Environment
6
+ # :category: Internal
7
+ TYPE = "worker"
8
+ # :category: Internal
9
+ TIER_NAME = "Worker"
10
+ # :category: Internal
11
+ TIER_TYPE = "SQS/HTTP"
12
+
4
13
  attr_reader :queue
5
14
 
6
15
  def initialize(name, queue:, **_)
@@ -8,13 +17,15 @@ module ElasticBeans
8
17
  @queue = queue
9
18
  end
10
19
 
11
- protected
20
+ # Returns "worker"
21
+ def type
22
+ TYPE
23
+ end
12
24
 
13
- TIER_NAME = "Worker"
14
- TIER_TYPE = "SQS/HTTP"
25
+ protected
15
26
 
16
27
  def template_name
17
- @template_name ||= "worker-#{queue}"
28
+ @template_name ||= "#{type}-#{queue}"
18
29
  end
19
30
 
20
31
  def tier_name