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