active_elastic_job_gunner 3.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 8d1a2ff186013ae24c5908c9c3f995fd4f98f284ac80bcb9d50ad4615d30d226
4
+ data.tar.gz: 8847a8c8627c257c45d16440ce31fe2cdc98b6281fbb9debf6421841354791a7
5
+ SHA512:
6
+ metadata.gz: c95ff1e79cb015d2db00fc81ff0d30fc2cb21ef9a1b61fe5e3979ed2084a6f2c38fbb397aa7ca2cee9f659f64ca04e356dd5d9d99e991cc806c34ae2f388cbc5
7
+ data.tar.gz: bcee34b110f398ffc2412a7035d5f5c2acbf840e57754bd832b57c531bd4d8b22f137e1dc3080bd4cdabf1c7c9780e8467f2389fa864152ce72e1d8a0299d9f6
@@ -0,0 +1,37 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ require 'active_elastic_job/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.platform = Gem::Platform::RUBY
9
+ spec.name = 'active_elastic_job_gunner'
10
+ spec.version = ActiveElasticJob.version
11
+ spec.authors = ['Cody Swann']
12
+ spec.email = ['cody@gunnertech.com']
13
+ spec.summary = 'Active Elastic Job is a simple to use Active Job backend for Rails applications deployed on the Amazon Elastic Beanstalk platform.'
14
+ spec.description = 'Run background jobs / tasks of Rails applications deployed in Amazon Elastic Beanstalk environments. Active Elastic Job is an Active Job backend which is easy to setup. No need for customised container commands or other workarounds.'
15
+ spec.license = 'MIT'
16
+ spec.homepage = 'https://github.com/gunnertech/active-elastic-job'
17
+
18
+ spec.files = Dir.glob('lib/**/*') + [ 'active-elastic-job.gemspec' ]
19
+ spec.executables = []
20
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
21
+ spec.require_paths = ['lib']
22
+
23
+ spec.required_ruby_version = '>= 2.5'
24
+
25
+ spec.add_dependency 'aws-sdk-sqs'
26
+ spec.add_dependency 'rails', '>= 5.0'
27
+
28
+ spec.add_development_dependency 'sqlite3'
29
+ spec.add_development_dependency 'bundler'
30
+ spec.add_development_dependency 'rspec', '~> 3.4'
31
+ spec.add_development_dependency 'dotenv'
32
+ spec.add_development_dependency 'fuubar'
33
+ spec.add_development_dependency 'rdoc'
34
+ spec.add_development_dependency 'byebug'
35
+ spec.add_development_dependency 'benchmark-ips'
36
+ spec.add_development_dependency 'climate_control'
37
+ end
@@ -0,0 +1,12 @@
1
+ require 'aws-sdk-sqs'
2
+ require 'active_elastic_job/version'
3
+ require 'active_elastic_job/md5_message_digest_calculation'
4
+ require 'active_job/queue_adapters/active_elastic_job_adapter'
5
+ require 'active_elastic_job/rack/sqs_message_consumer'
6
+ require 'active_elastic_job/message_verifier'
7
+
8
+ module ActiveElasticJob
9
+ ACRONYM = 'AEJ'.freeze
10
+ end
11
+
12
+ require "active_elastic_job/railtie" if defined? Rails
@@ -0,0 +1,72 @@
1
+ require 'openssl'
2
+ module ActiveElasticJob
3
+ # This module provides methods that calculate the MD5 digest for Amazon
4
+ # SQS message bodies and message attributes.
5
+ # The digest can be used to verify that Amazon SQS received the message
6
+ # correctly.
7
+ #
8
+ # Example:
9
+ #
10
+ # extend ActiveElasticJob::MD5MessageDigestCalculation
11
+ #
12
+ # resp = Aws::SQS::Client.new.send_message(
13
+ # queue_url: queue_url,
14
+ # message_body: body,
15
+ # message_attributes: attributes
16
+ # )
17
+ #
18
+ # if resp.md5_of_message_body != md5_of_message_body(body)
19
+ # raise "Returned digest of message body is invalid!"
20
+ # end
21
+ #
22
+ # if resp.md5_of_message_attributes != md5_of_message_attributes(attributes)
23
+ # raise "Returned digest of message attributes is invalid!"
24
+ # end
25
+ module MD5MessageDigestCalculation
26
+ TRANSPORT_TYPE_ENCODINGS = {
27
+ 'String' => 1,
28
+ 'Binary' => 2,
29
+ 'Number' => 1
30
+ }
31
+
32
+ NORMALIZED_ENCODING = Encoding::UTF_8
33
+
34
+ def md5_of_message_body(message_body)
35
+ OpenSSL::Digest::MD5.hexdigest(message_body)
36
+ end
37
+
38
+ def md5_of_message_attributes(message_attributes)
39
+ encoded = { }
40
+ message_attributes.each do |name, attribute|
41
+ name = name.to_s
42
+ encoded[name] = String.new
43
+ encoded[name] << encode_length_and_bytes(name) <<
44
+ encode_length_and_bytes(attribute[:data_type]) <<
45
+ [TRANSPORT_TYPE_ENCODINGS[attribute[:data_type]]].pack('C'.freeze)
46
+
47
+ if attribute[:string_value] != nil
48
+ encoded[name] << encode_length_and_string(attribute[:string_value])
49
+ elsif attribute[:binary_value] != nil
50
+ encoded[name] << encode_length_and_bytes(attribute[:binary_value])
51
+ end
52
+ end
53
+
54
+ buffer = encoded.keys.sort.reduce(String.new) do |string, name|
55
+ string << encoded[name]
56
+ end
57
+ OpenSSL::Digest::MD5.hexdigest(buffer)
58
+ end
59
+
60
+ private
61
+
62
+ def encode_length_and_string(string)
63
+ string = String.new(string)
64
+ string.encode!(NORMALIZED_ENCODING)
65
+ encode_length_and_bytes(string)
66
+ end
67
+
68
+ def encode_length_and_bytes(bytes)
69
+ [bytes.bytesize, bytes].pack('L>a*'.freeze)
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,34 @@
1
+ require 'active_support/security_utils'
2
+
3
+ module ActiveElasticJob
4
+ class MessageVerifier #:nodoc:
5
+
6
+ # Raised when digest generated by
7
+ # <tt>ActiveJob::QueueAdapters::ActiveElasticJobAdapter</tt> could not
8
+ # be verified.
9
+ class InvalidDigest < StandardError
10
+ end
11
+
12
+ def initialize(secret)
13
+ @secret = secret
14
+ end
15
+
16
+ def verify(message, digest)
17
+ if message.nil? || message.blank? || digest.nil? || digest.blank?
18
+ return false
19
+ end
20
+
21
+ return ActiveSupport::SecurityUtils.secure_compare(
22
+ digest, generate_digest(message))
23
+ end
24
+
25
+ def verify!(message, digest)
26
+ raise InvalidDigest unless verify(message, digest)
27
+ end
28
+
29
+ def generate_digest(message)
30
+ require 'openssl' unless defined?(OpenSSL)
31
+ OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA1.new, @secret, message)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,129 @@
1
+ require "action_dispatch"
2
+
3
+ module ActiveElasticJob
4
+ module Rack
5
+ # This middleware intercepts requests which are sent by the SQS daemon
6
+ # running in {Amazon Elastic Beanstalk worker environments}[http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/using-features-managing-env-tiers.html].
7
+ # It does this by looking at the +User-Agent+ header.
8
+ # Requesets from the SQS daemon are handled in two alternative cases:
9
+ #
10
+ # (1) the processed SQS message was originally triggered by a periodic task
11
+ # supported by Elastic Beanstalk's Periodic Task feature
12
+ #
13
+ # (2) the processed SQS message was queued by this gem representing an active job.
14
+ # In this case it verifies the digest which is sent along with a legit SQS
15
+ # message, and passed as an HTTP header in the resulting request.
16
+ # The digest is based on Rails' +secrets.secret_key_base+.
17
+ # Therefore, the application running in the web environment, which generates
18
+ # the digest, and the application running in the worker
19
+ # environment, which verifies the digest, have to use the *same*
20
+ # +secrets.secret_key_base+ setting.
21
+ class SqsMessageConsumer
22
+ OK_RESPONSE = [ '200'.freeze, { 'Content-Type'.freeze => 'text/plain'.freeze }, [ 'OK'.freeze ] ]
23
+ FORBIDDEN_RESPONSE = [
24
+ '403'.freeze,
25
+ { 'Content-Type'.freeze => 'text/plain'.freeze },
26
+ [ 'Request forbidden!'.freeze ]
27
+ ]
28
+ DOCKER_HOST_IP = /172.17.0.\d+/.freeze
29
+
30
+ def initialize(app) #:nodoc:
31
+ @app = app
32
+ end
33
+
34
+ def call(env) #:nodoc:
35
+ request = ActionDispatch::Request.new env
36
+ if enabled? && aws_sqsd?(request)
37
+ unless request.local? || sent_from_docker_host?(request)
38
+ return FORBIDDEN_RESPONSE
39
+ end
40
+
41
+ if periodic_task?(request)
42
+ execute_periodic_task(request)
43
+ return OK_RESPONSE
44
+ elsif originates_from_gem?(request)
45
+ begin
46
+ execute_job(request)
47
+ rescue ActiveElasticJob::MessageVerifier::InvalidDigest => e
48
+ return FORBIDDEN_RESPONSE
49
+ end
50
+ return OK_RESPONSE
51
+ end
52
+ end
53
+ @app.call(env)
54
+ end
55
+
56
+ private
57
+
58
+ def enabled?
59
+ Rails.application.config.active_elastic_job.process_jobs == true
60
+ end
61
+
62
+ def verify!(request)
63
+ @verifier ||= ActiveElasticJob::MessageVerifier.new(secret_key_base)
64
+ digest = request.headers['HTTP_X_AWS_SQSD_ATTR_MESSAGE_DIGEST'.freeze]
65
+ message = request.body_stream.read
66
+ request.body_stream.rewind
67
+ @verifier.verify!(message, digest)
68
+ end
69
+
70
+ def secret_key_base
71
+ config.secret_key_base
72
+ end
73
+
74
+ def config
75
+ Rails.application.config.active_elastic_job
76
+ end
77
+
78
+ def aws_sqsd?(request)
79
+ # Does not match against a Regexp
80
+ # in order to avoid performance penalties.
81
+ # Instead performs a simple string comparison.
82
+ # Benchmark runs showed an performance increase of
83
+ # up to 40%
84
+ current_user_agent = request.headers['User-Agent'.freeze]
85
+ return (current_user_agent.present? &&
86
+ current_user_agent.size >= 'aws-sqsd'.freeze.size &&
87
+ current_user_agent[0..('aws-sqsd'.freeze.size - 1)] == 'aws-sqsd'.freeze)
88
+ end
89
+
90
+ def periodic_tasks_route
91
+ @periodic_tasks_route ||= config.periodic_tasks_route
92
+ end
93
+
94
+ def periodic_task?(request)
95
+ !request.fullpath.nil? && request.fullpath[0..(periodic_tasks_route.size - 1)] == periodic_tasks_route
96
+ end
97
+
98
+ def execute_job(request)
99
+ verify!(request)
100
+ job = JSON.load(request.body)
101
+ ActiveJob::Base.execute(job)
102
+ end
103
+
104
+ def execute_periodic_task(request)
105
+ job_name = request.headers['X-Aws-Sqsd-Taskname']
106
+ job = job_name.constantize.new
107
+ job.perform_now
108
+ end
109
+
110
+ def originates_from_gem?(request)
111
+ if request.headers['HTTP_X_AWS_SQSD_ATTR_ORIGIN'.freeze] == ActiveElasticJob::ACRONYM
112
+ return true
113
+ elsif request.headers['HTTP_X_AWS_SQSD_ATTR_MESSAGE_DIGEST'.freeze] != nil
114
+ return true
115
+ else
116
+ return false
117
+ end
118
+ end
119
+
120
+ def sent_from_docker_host?(request)
121
+ app_runs_in_docker_container? && request.remote_ip =~ DOCKER_HOST_IP
122
+ end
123
+
124
+ def app_runs_in_docker_container?
125
+ @app_in_docker_container ||= `[ -f /proc/1/cgroup ] && cat /proc/1/cgroup` =~ /(ecs|docker)/
126
+ end
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,25 @@
1
+ module ActiveElasticJob
2
+ class Railtie < Rails::Railtie
3
+ config.active_elastic_job = ActiveSupport::OrderedOptions.new
4
+ process_active_elastic_jobs = ENV['PROCESS_ACTIVE_ELASTIC_JOBS']
5
+ config.active_elastic_job.process_jobs = !process_active_elastic_jobs.nil? && process_active_elastic_jobs.downcase == 'true'
6
+ config.active_elastic_job.aws_credentials = lambda { Aws::InstanceProfileCredentials.new }
7
+ config.active_elastic_job.aws_region = ENV['AWS_REGION']
8
+ config.active_elastic_job.periodic_tasks_route = '/periodic_tasks'.freeze
9
+
10
+ initializer "active_elastic_job.insert_middleware" do |app|
11
+ if app.config.active_elastic_job.secret_key_base.blank?
12
+ app.config.active_elastic_job.secret_key_base = app.secrets[:secret_key_base]
13
+ end
14
+
15
+ if app.config.active_elastic_job.process_jobs == true
16
+ app.config.active_elastic_job.aws_credentials ||= lambda { Aws::InstanceProfileCredentials.new }
17
+ if app.config.force_ssl
18
+ app.config.middleware.insert_before(ActionDispatch::SSL,ActiveElasticJob::Rack::SqsMessageConsumer)
19
+ else
20
+ app.config.middleware.use(ActiveElasticJob::Rack::SqsMessageConsumer)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,18 @@
1
+ module ActiveElasticJob
2
+ module VERSION
3
+ MAJOR = 3
4
+ MINOR = 0
5
+ TINY = 1
6
+ PRE = nil
7
+
8
+ STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
9
+
10
+ def self.to_s
11
+ STRING
12
+ end
13
+ end
14
+
15
+ def self.version
16
+ VERSION::STRING
17
+ end
18
+ end
@@ -0,0 +1,228 @@
1
+ module ActiveJob
2
+ module QueueAdapters
3
+ # == Active Elastic Job adapter for Active Job
4
+ #
5
+ # Active Elastic Job provides (1) an adapter (this class) for Rails'
6
+ # Active Job framework and (2) a Rack middleware to process job requests,
7
+ # which are sent by the SQS daemon running in {Amazon Elastic Beanstalk worker
8
+ # environments}[http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/using-features-managing-env-tiers.html].
9
+ #
10
+ # This adapter serializes job objects and sends them as a message to an
11
+ # Amazon SQS queue specified by the job's queue name,
12
+ # see <tt>ActiveJob::Base.queue_as</tt>
13
+ #
14
+ # To use Active Elastic Job, set the queue_adapter config
15
+ # to +:active_elastic_job+.
16
+ #
17
+ # Rails.application.config.active_job.queue_adapter = :active_elastic_job
18
+ class ActiveElasticJobAdapter
19
+ MAX_MESSAGE_SIZE = (256 * 1024)
20
+ MAX_DELAY_IN_MINUTES = 15
21
+
22
+ extend ActiveElasticJob::MD5MessageDigestCalculation
23
+
24
+ class Error < RuntimeError; end;
25
+
26
+ # Raised when job exceeds 256 KB in its serialized form. The limit is
27
+ # imposed by Amazon SQS.
28
+ class SerializedJobTooBig < Error
29
+ def initialize(serialized_job)
30
+ super(<<-MSG)
31
+ The job contains #{serialized_job.bytesize} bytes in its serialized form,
32
+ which exceeds the allowed maximum of #{MAX_MESSAGE_SIZE} bytes imposed by Amazon SQS.
33
+ MSG
34
+ end
35
+ end
36
+
37
+ # Raised when job queue does not exist. The job queue is determined by
38
+ # <tt>ActiveJob::Base.queue_as</tt>. You can either: (1) create a new
39
+ # Amazon SQS queue and attach a worker environment to it, or (2) select a
40
+ # different queue for your jobs.
41
+ #
42
+ # Example:
43
+ # * Open your AWS console and create an SQS queue named +high_priority+ in
44
+ # the same AWS region of your Elastic Beanstalk environments.
45
+ # * Queue your jobs accordingly:
46
+ #
47
+ # class MyJob < ActiveJob::Base
48
+ # queue_as :high_priority
49
+ # #..
50
+ # end
51
+ class NonExistentQueue < Error
52
+ def initialize(queue_name, aws_region)
53
+
54
+ super(<<-MSG)
55
+ The job is bound to queue at #{queue_name}.
56
+ Unfortunately a queue with this name does not exist in this
57
+ region. Either create an Amazon SQS queue named #{queue_name} -
58
+ you can do this in AWS console, make sure to select region
59
+ '#{aws_region}' - or you
60
+ select another queue for your jobs.
61
+ MSG
62
+ end
63
+ end
64
+
65
+ # Raised when calculated MD5 digest does not match the MD5 Digest
66
+ # of the response from Amazon SQS.
67
+ class MD5MismatchError < Error
68
+ def initialize(message_id, calculated, returned)
69
+
70
+ super(<<-MSG)
71
+ MD5 '#{returned}' returned by Amazon SQS does not match the
72
+ calculation on the original request which was '#{calculated}'.
73
+ The message with Message ID #{message_id} sent to SQS might be
74
+ corrupted.
75
+ MSG
76
+ end
77
+ end
78
+
79
+ # Raised when the delay is longer than the MAX_DELAY_IN_MINUTES
80
+ class DelayTooLong < RangeError
81
+ def initialize()
82
+ super(<<-MSG)
83
+ Jobs cannot be scheduled more than #{MAX_DELAY_IN_MINUTES} minutes
84
+ into the future.
85
+ See http://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_SendMessage.html
86
+ for further details!
87
+ MSG
88
+ end
89
+ end
90
+
91
+ def enqueue(job) #:nodoc:
92
+ self.class.enqueue job
93
+ end
94
+
95
+ def enqueue_at(job, timestamp) #:nodoc:
96
+ self.class.enqueue_at(job, timestamp)
97
+ end
98
+
99
+ class << self
100
+ def enqueue(job) #:nodoc:
101
+ enqueue_at(job, Time.now)
102
+ end
103
+
104
+ def enqueue_at(job, timestamp) #:nodoc:
105
+ serialized_job = JSON.dump(job.serialize)
106
+ check_job_size!(serialized_job)
107
+ message = build_message(job.queue_name, serialized_job, timestamp)
108
+ resp = aws_sqs_client.send_message(message)
109
+ unless aws_client_verifies_md5_digests?
110
+ verify_md5_digests!(
111
+ resp,
112
+ message[:message_body],
113
+ message[:message_attributes])
114
+ end
115
+ job.provider_job_id = resp.message_id
116
+ rescue Aws::SQS::Errors::NonExistentQueue => e
117
+ unless @queue_urls[job.queue_name.to_s].nil?
118
+ @queue_urls[job.queue_name.to_s] = nil
119
+ retry
120
+ end
121
+ raise NonExistentQueue.new(job, aws_region)
122
+ rescue Aws::Errors::ServiceError => e
123
+ raise Error, "Could not enqueue job, #{e.message}"
124
+ end
125
+
126
+ private
127
+
128
+ def aws_client_verifies_md5_digests?
129
+ Gem::Version.new(Aws::CORE_GEM_VERSION) >= Gem::Version.new('2.2.19'.freeze)
130
+ end
131
+
132
+ def build_message(queue_name, serialized_job, timestamp)
133
+ {
134
+ queue_url: queue_url(queue_name),
135
+ message_body: serialized_job,
136
+ delay_seconds: calculate_delay(timestamp),
137
+ message_attributes: {
138
+ "message-digest".freeze => {
139
+ string_value: message_digest(serialized_job),
140
+ data_type: "String".freeze
141
+ },
142
+ origin: {
143
+ string_value: ActiveElasticJob::ACRONYM,
144
+ data_type: "String".freeze
145
+ }
146
+ }
147
+ }
148
+ end
149
+
150
+ def queue_url(queue_name)
151
+ cache_key = queue_name.to_s
152
+ @queue_urls ||= { }
153
+ return @queue_urls[cache_key] if @queue_urls[cache_key]
154
+ resp = aws_sqs_client.get_queue_url(queue_name: queue_name.to_s)
155
+ @queue_urls[cache_key] = resp.queue_url
156
+ rescue Aws::SQS::Errors::NonExistentQueue => e
157
+ raise NonExistentQueue.new(queue_name, aws_region)
158
+ end
159
+
160
+ def calculate_delay(timestamp)
161
+ delay = (timestamp - Time.current.to_f).to_i + 1
162
+ if delay > MAX_DELAY_IN_MINUTES.minutes
163
+ raise DelayTooLong.new
164
+ end
165
+ delay = 0 if delay < 0
166
+ delay
167
+ end
168
+
169
+ def check_job_size!(serialized_job)
170
+ if serialized_job.bytesize > MAX_MESSAGE_SIZE
171
+ raise SerializedJobTooBig, serialized_job
172
+ end
173
+ end
174
+
175
+ def aws_sqs_client
176
+ options = {
177
+ credentials: aws_sqs_client_credentials,
178
+ region: aws_region
179
+ }
180
+ endpoint = Rails.application.config.active_elastic_job.endpoint
181
+ options[:endpoint] = endpoint if endpoint.present?
182
+ @aws_sqs_client ||= Aws::SQS::Client.new(options)
183
+ end
184
+
185
+ def aws_sqs_client_credentials
186
+ @aws_credentials ||= if config.aws_credentials.kind_of?(Proc)
187
+ config.aws_credentials.call
188
+ else
189
+ config.aws_credentials
190
+ end
191
+ end
192
+
193
+ def aws_region
194
+ config.aws_region
195
+ end
196
+
197
+ def config
198
+ Rails.application.config.active_elastic_job
199
+ end
200
+
201
+ def message_digest(messsage_body)
202
+ @verifier ||= ActiveElasticJob::MessageVerifier.new(secret_key_base)
203
+ @verifier.generate_digest(messsage_body)
204
+ end
205
+
206
+ def verify_md5_digests!(response, messsage_body, message_attributes)
207
+ calculated = md5_of_message_body(messsage_body)
208
+ returned = response.md5_of_message_body
209
+ if calculated != returned
210
+ raise MD5MismatchError.new response.message_id, calculated, returned
211
+ end
212
+
213
+ if message_attributes
214
+ calculated = md5_of_message_attributes(message_attributes)
215
+ returned = response.md5_of_message_attributes
216
+ if calculated != returned
217
+ raise MD5MismatchError.new response.message_id, calculated, returned
218
+ end
219
+ end
220
+ end
221
+
222
+ def secret_key_base
223
+ config.secret_key_base
224
+ end
225
+ end
226
+ end
227
+ end
228
+ end
metadata ADDED
@@ -0,0 +1,208 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: active_elastic_job_gunner
3
+ version: !ruby/object:Gem::Version
4
+ version: 3.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Cody Swann
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-02-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: aws-sdk-sqs
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rails
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '5.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '5.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: sqlite3
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.4'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.4'
83
+ - !ruby/object:Gem::Dependency
84
+ name: dotenv
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: fuubar
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rdoc
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: byebug
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: benchmark-ips
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: climate_control
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ description: Run background jobs / tasks of Rails applications deployed in Amazon
168
+ Elastic Beanstalk environments. Active Elastic Job is an Active Job backend which
169
+ is easy to setup. No need for customised container commands or other workarounds.
170
+ email:
171
+ - cody@gunnertech.com
172
+ executables: []
173
+ extensions: []
174
+ extra_rdoc_files: []
175
+ files:
176
+ - active-elastic-job.gemspec
177
+ - lib/active_elastic_job.rb
178
+ - lib/active_elastic_job/md5_message_digest_calculation.rb
179
+ - lib/active_elastic_job/message_verifier.rb
180
+ - lib/active_elastic_job/rack/sqs_message_consumer.rb
181
+ - lib/active_elastic_job/railtie.rb
182
+ - lib/active_elastic_job/version.rb
183
+ - lib/active_job/queue_adapters/active_elastic_job_adapter.rb
184
+ homepage: https://github.com/gunnertech/active-elastic-job
185
+ licenses:
186
+ - MIT
187
+ metadata: {}
188
+ post_install_message:
189
+ rdoc_options: []
190
+ require_paths:
191
+ - lib
192
+ required_ruby_version: !ruby/object:Gem::Requirement
193
+ requirements:
194
+ - - ">="
195
+ - !ruby/object:Gem::Version
196
+ version: '2.5'
197
+ required_rubygems_version: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - ">="
200
+ - !ruby/object:Gem::Version
201
+ version: '0'
202
+ requirements: []
203
+ rubygems_version: 3.1.4
204
+ signing_key:
205
+ specification_version: 4
206
+ summary: Active Elastic Job is a simple to use Active Job backend for Rails applications
207
+ deployed on the Amazon Elastic Beanstalk platform.
208
+ test_files: []