active_elastic_job 1.7.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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