active_elastic_job 1.7.0 → 2.0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 33d8c9fbb99bf26f435b6ec527e34022b1967473
4
- data.tar.gz: e87b6fb000e3ee8feac2b549af57b83852358c72
3
+ metadata.gz: d8e455f174d4846927c18875dd2e1f645a87f870
4
+ data.tar.gz: 3cd316cd0b53a07cbf66234ccbb2ec71f5fe6e61
5
5
  SHA512:
6
- metadata.gz: ca2572266cbfd44cfa04270ece01d0e99c26f6768ac6d20aa4f7fd3969130ff5afb2d82f11ac3ba092ddf2378dedeada37c344844a677aebf4e73b650ee50cde
7
- data.tar.gz: 068c7a6062081d4d0196d4a865179515f89b9a93375aa55574cd6a724c9316295d56a3b277aaf01c9debf882ed337e00741caf6f83e8111517b2bc920b38fa23
6
+ metadata.gz: d7ef1844c86ce29e4a93e0184eca63e879b0d43bcc08d1e35ab7573a08975e6943f8eb489028d4f47960780ab60743cbefc760961dd25b3cf49ed49500a00d9b
7
+ data.tar.gz: e0e5d7390144a3ebf53c756dc7a56436e14dabf4a6e2e37765fefff5625568a93669b7c8623b1f3aa1124c148938468e46d9059d59f604d2f85cb89b9d917798
@@ -5,7 +5,13 @@ module ActiveElasticJob
5
5
  # This middleware intercepts requests which are sent by the SQS daemon
6
6
  # running in {Amazon Elastic Beanstalk worker environments}[http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/using-features-managing-env-tiers.html].
7
7
  # It does this by looking at the +User-Agent+ header.
8
- # Furthermore, it verifies the digest which is sent along with a legit SQS
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
9
15
  # message, and passed as an HTTP header in the resulting request.
10
16
  # The digest is based on Rails' +secrets.secret_key_base+.
11
17
  # Therefore, the application running in the web environment, which generates
@@ -13,14 +19,13 @@ module ActiveElasticJob
13
19
  # environment, which verifies the digest, have to use the *same*
14
20
  # +secrets.secret_key_base+ setting.
15
21
  class SqsMessageConsumer
16
- USER_AGENT_PREFIX = 'aws-sqsd'.freeze
17
- DIGEST_HEADER_NAME = 'HTTP_X_AWS_SQSD_ATTR_MESSAGE_DIGEST'.freeze
18
- ORIGIN_HEADER_NAME = 'HTTP_X_AWS_SQSD_ATTR_ORIGIN'.freeze
19
- CONTENT_TYPE = 'application/json'.freeze
20
- CONTENT_TYPE_HEADER_NAME = 'Content-Type'.freeze
21
- OK_RESPONSE_CODE = '200'.freeze
22
- INSIDE_DOCKER_CONTAINER = `[ -f /proc/1/cgroup ] && cat /proc/1/cgroup` =~ /docker/
23
- DOCKER_HOST_IP = "172.17.0.1".freeze
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.1'.freeze
24
29
 
25
30
  def initialize(app) #:nodoc:
26
31
  @app = app
@@ -28,25 +33,22 @@ module ActiveElasticJob
28
33
 
29
34
  def call(env) #:nodoc:
30
35
  request = ActionDispatch::Request.new env
31
- if enabled? && aws_sqsd?(request) && originates_from_gem?(request)
36
+ if enabled? && aws_sqsd?(request)
32
37
  unless request.local? || sent_from_docker_host?(request)
33
- m = "Accepts only requests from localhost for job processing".freeze
34
- return ['403', {CONTENT_TYPE_HEADER_NAME => 'text/plain' }, [ m ]]
38
+ return FORBIDDEN_RESPONSE
35
39
  end
36
- begin
37
- verify!(request)
38
- job = JSON.load(request.body)
39
- ActiveJob::Base.execute(job)
40
- rescue ActiveElasticJob::MessageVerifier::InvalidDigest => e
41
- return [
42
- '403',
43
- {CONTENT_TYPE_HEADER_NAME => 'text/plain' },
44
- ["incorrect digest"]]
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
45
51
  end
46
- return [
47
- OK_RESPONSE_CODE ,
48
- {CONTENT_TYPE_HEADER_NAME => CONTENT_TYPE },
49
- [ '' ]]
50
52
  end
51
53
  @app.call(env)
52
54
  end
@@ -54,35 +56,61 @@ module ActiveElasticJob
54
56
  private
55
57
 
56
58
  def enabled?
57
- var = ENV['DISABLE_SQS_CONSUMER'.freeze]
58
- var == nil || var == 'false'.freeze
59
+ Rails.application.config.active_elastic_job.process_jobs == true
59
60
  end
60
61
 
61
62
  def verify!(request)
62
- secret_key_base = Rails.application.secrets[:secret_key_base]
63
63
  @verifier ||= ActiveElasticJob::MessageVerifier.new(secret_key_base)
64
- digest = request.headers[DIGEST_HEADER_NAME]
64
+ digest = request.headers['HTTP_X_AWS_SQSD_ATTR_MESSAGE_DIGEST'.freeze]
65
65
  message = request.body_stream.read
66
66
  request.body_stream.rewind
67
67
  @verifier.verify!(message, digest)
68
68
  end
69
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
+
70
78
  def aws_sqsd?(request)
71
- # we do not match against a Regexp
79
+ # Does not match against a Regexp
72
80
  # in order to avoid performance penalties.
73
- # Instead we make a simple string comparison.
81
+ # Instead performs a simple string comparison.
74
82
  # Benchmark runs showed an performance increase of
75
83
  # up to 40%
76
84
  current_user_agent = request.headers['User-Agent'.freeze]
77
85
  return (current_user_agent.present? &&
78
- current_user_agent.size >= USER_AGENT_PREFIX.size &&
79
- current_user_agent[0..(USER_AGENT_PREFIX.size - 1)] == USER_AGENT_PREFIX)
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
80
108
  end
81
109
 
82
110
  def originates_from_gem?(request)
83
- if request.headers[ORIGIN_HEADER_NAME] == ActiveElasticJob::ACRONYM
111
+ if request.headers['HTTP_X_AWS_SQSD_ATTR_ORIGIN'.freeze] == ActiveElasticJob::ACRONYM
84
112
  return true
85
- elsif request.headers[DIGEST_HEADER_NAME] != nil
113
+ elsif request.headers['HTTP_X_AWS_SQSD_ATTR_MESSAGE_DIGEST'.freeze] != nil
86
114
  return true
87
115
  else
88
116
  return false
@@ -90,7 +118,11 @@ module ActiveElasticJob
90
118
  end
91
119
 
92
120
  def sent_from_docker_host?(request)
93
- INSIDE_DOCKER_CONTAINER && request.remote_ip == DOCKER_HOST_IP
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` =~ /docker/
94
126
  end
95
127
  end
96
128
  end
@@ -1,14 +1,16 @@
1
1
  module ActiveElasticJob
2
2
  class Railtie < Rails::Railtie
3
3
  config.active_elastic_job = ActiveSupport::OrderedOptions.new
4
- config.active_elastic_job.aws_region = ENV['AWS_REGION']
5
- config.active_elastic_job.aws_access_key_id = ENV['AWS_ACCESS_KEY_ID']
6
- config.active_elastic_job.aws_secret_access_key = ENV['AWS_SECRET_ACCESS_KEY'] || ENV['AWS_SECRET_KEY'] || ENV['AMAZON_SECRET_ACCESS_KEY']
7
- config.active_elastic_job.disable_sqs_confumer = ENV['DISABLE_SQS_CONSUMER']
4
+ config.active_elastic_job.process_jobs = ENV['PROCESS_ACTIVE_ELASTIC_JOBS'] == 'true' || false
5
+ config.active_elastic_job.aws_credentials = Aws::InstanceProfileCredentials.new
6
+ config.active_elastic_job.periodic_tasks_route = '/periodic_tasks'.freeze
8
7
 
9
8
  initializer "active_elastic_job.insert_middleware" do |app|
10
- disabled = app.config.active_elastic_job.disable_sqs_confumer
11
- if disabled == 'false' || disabled.nil?
9
+ if app.config.active_elastic_job.secret_key_base.blank?
10
+ app.config.active_elastic_job.secret_key_base = app.secrets[:secret_key_base]
11
+ end
12
+
13
+ if app.config.active_elastic_job.process_jobs == true
12
14
  if app.config.force_ssl
13
15
  app.config.middleware.insert_before(ActionDispatch::SSL,ActiveElasticJob::Rack::SqsMessageConsumer)
14
16
  else
@@ -1,7 +1,7 @@
1
1
  module ActiveElasticJob
2
2
  module VERSION
3
- MAJOR = 1
4
- MINOR = 7
3
+ MAJOR = 2
4
+ MINOR = 0
5
5
  TINY = 0
6
6
  PRE = nil
7
7
 
@@ -19,12 +19,6 @@ module ActiveJob
19
19
  MAX_MESSAGE_SIZE = (256 * 1024)
20
20
  MAX_DELAY_IN_MINUTES = 15
21
21
 
22
- if Gem::Version.new(Aws::VERSION) >= Gem::Version.new('2.2.19')
23
- AWS_CLIENT_VERIFIES_MD5_DIGESTS = true
24
- else
25
- AWS_CLIENT_VERIFIES_MD5_DIGESTS = false
26
- end
27
-
28
22
  extend ActiveElasticJob::MD5MessageDigestCalculation
29
23
 
30
24
  class Error < RuntimeError; end;
@@ -56,14 +50,15 @@ module ActiveJob
56
50
  # #..
57
51
  # end
58
52
  class NonExistentQueue < Error
59
- def initialize(queue_name)
53
+ def initialize(queue_name, aws_region)
60
54
 
61
55
  super(<<-MSG)
62
56
  The job is bound to queue at #{queue_name}.
63
57
  Unfortunately a queue with this name does not exist in this
64
58
  region. Either create an Amazon SQS queue named #{queue_name} -
65
59
  you can do this in AWS console, make sure to select region
66
- '#{ENV['AWS_REGION']}' - or you select another queue for your jobs.
60
+ '#{aws_region}' - or you
61
+ select another queue for your jobs.
67
62
  MSG
68
63
  end
69
64
  end
@@ -111,7 +106,7 @@ module ActiveJob
111
106
  @queue_urls[job.queue_name.to_s] = nil
112
107
  retry
113
108
  end
114
- raise NonExistentQueue, job
109
+ raise NonExistentQueue.new(job, aws_region)
115
110
  rescue Aws::Errors::ServiceError => e
116
111
  raise Error, "Could not enqueue job, #{e.message}"
117
112
  end
@@ -119,7 +114,7 @@ module ActiveJob
119
114
  private
120
115
 
121
116
  def aws_client_verifies_md5_digests?
122
- return AWS_CLIENT_VERIFIES_MD5_DIGESTS
117
+ Gem::Version.new(Aws::VERSION) >= Gem::Version.new('2.2.19'.freeze)
123
118
  end
124
119
 
125
120
  def build_message(queue_name, serialized_job, timestamp)
@@ -171,17 +166,19 @@ module ActiveJob
171
166
  end
172
167
 
173
168
  def aws_sqs_client
174
- c = Rails.application.config.active_elastic_job
175
- @aws_sqs_client ||= if c.aws_credentials.present?
176
- Aws::SQS::Client.new(
177
- credentials: c.aws_credentials,
178
- region: c.aws_region)
179
- else
180
- Aws::SQS::Client.new(
181
- access_key_id: c.aws_access_key_id,
182
- secret_access_key: c.aws_secret_access_key,
183
- region: c.aws_region)
184
- end
169
+ @aws_sqs_client ||= Aws::SQS::Client.new(credentials: aws_sqs_client_credentials )
170
+ end
171
+
172
+ def aws_sqs_client_credentials
173
+ config.aws_credentials
174
+ end
175
+
176
+ def aws_region
177
+ config.aws_region
178
+ end
179
+
180
+ def config
181
+ Rails.application.config.active_elastic_job
185
182
  end
186
183
 
187
184
  def message_digest(messsage_body)
@@ -206,7 +203,7 @@ module ActiveJob
206
203
  end
207
204
 
208
205
  def secret_key_base
209
- @secret_key_base ||= Rails.application.secrets[:secret_key_base]
206
+ config.secret_key_base
210
207
  end
211
208
  end
212
209
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_elastic_job
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.7.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tawan Sierek
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-11-05 00:00:00.000000000 Z
11
+ date: 2016-11-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aws-sdk