cloudtasker 0.8.0 → 0.9.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- cloudtasker (0.2.0)
4
+ cloudtasker (0.9.2)
5
5
  activesupport
6
6
  fugit
7
7
  google-cloud-tasks
@@ -73,23 +73,33 @@ GEM
73
73
  rake
74
74
  thor (>= 0.14.0)
75
75
  ast (2.4.0)
76
- builder (3.2.3)
77
- concurrent-ruby (1.1.5)
76
+ builder (3.2.4)
77
+ concurrent-ruby (1.1.6)
78
78
  crack (0.4.3)
79
79
  safe_yaml (~> 1.0.0)
80
- crass (1.0.5)
80
+ crass (1.0.6)
81
81
  diff-lcs (1.3)
82
82
  erubi (1.9.0)
83
83
  et-orbi (1.2.2)
84
84
  tzinfo
85
- faraday (0.17.0)
85
+ faraday (0.17.3)
86
86
  multipart-post (>= 1.2, < 3)
87
+ faraday-http-cache (2.0.0)
88
+ faraday (~> 0.8)
87
89
  fugit (1.3.3)
88
90
  et-orbi (~> 1.1, >= 1.1.8)
89
91
  raabro (~> 1.1)
92
+ github_changelog_generator (1.15.0)
93
+ activesupport
94
+ faraday-http-cache
95
+ multi_json
96
+ octokit (~> 4.6)
97
+ rainbow (>= 2.2.1)
98
+ rake (>= 10.0)
99
+ retriable (~> 3.0)
90
100
  globalid (0.4.2)
91
101
  activesupport (>= 4.2.0)
92
- google-cloud-tasks (1.3.1)
102
+ google-cloud-tasks (1.4.0)
93
103
  google-gax (~> 1.8)
94
104
  googleapis-common-protos (>= 1.3.9, < 2.0)
95
105
  googleapis-common-protos-types (>= 1.0.4, < 2.0)
@@ -100,56 +110,59 @@ GEM
100
110
  googleauth (~> 0.9)
101
111
  grpc (~> 1.24)
102
112
  rly (~> 0.2.3)
103
- google-protobuf (3.10.1-universal-darwin)
113
+ google-protobuf (3.11.4)
104
114
  googleapis-common-protos (1.3.9)
105
115
  google-protobuf (~> 3.0)
106
116
  googleapis-common-protos-types (~> 1.0)
107
117
  grpc (~> 1.0)
108
118
  googleapis-common-protos-types (1.0.4)
109
119
  google-protobuf (~> 3.0)
110
- googleauth (0.10.0)
111
- faraday (~> 0.12)
120
+ googleauth (0.11.0)
121
+ faraday (>= 0.17.3, < 2.0)
112
122
  jwt (>= 1.4, < 3.0)
113
123
  memoist (~> 0.16)
114
124
  multi_json (~> 1.11)
115
125
  os (>= 0.9, < 2.0)
116
126
  signet (~> 0.12)
117
- grpc (1.25.0-universal-darwin)
118
- google-protobuf (~> 3.8)
127
+ grpc (1.27.0)
128
+ google-protobuf (~> 3.11)
119
129
  googleapis-common-protos-types (~> 1.0)
120
130
  grpc-google-iam-v1 (0.6.9)
121
131
  googleapis-common-protos (>= 1.3.1, < 2.0)
122
132
  grpc (~> 1.0)
123
- hashdiff (1.0.0)
124
- i18n (1.7.0)
133
+ hashdiff (1.0.1)
134
+ i18n (1.8.2)
125
135
  concurrent-ruby (~> 1.0)
126
136
  jaro_winkler (1.5.4)
127
137
  jwt (2.2.1)
128
- loofah (2.3.1)
138
+ loofah (2.4.0)
129
139
  crass (~> 1.0.2)
130
140
  nokogiri (>= 1.5.9)
131
141
  mail (2.7.1)
132
142
  mini_mime (>= 0.1.1)
133
143
  marcel (0.3.3)
134
144
  mimemagic (~> 0.3.2)
135
- memoist (0.16.1)
145
+ memoist (0.16.2)
136
146
  method_source (0.9.2)
137
- mimemagic (0.3.3)
147
+ mimemagic (0.3.4)
138
148
  mini_mime (1.0.2)
139
149
  mini_portile2 (2.4.0)
140
- minitest (5.13.0)
150
+ minitest (5.14.0)
141
151
  multi_json (1.14.1)
142
152
  multipart-post (2.1.1)
143
153
  nio4r (2.5.2)
144
- nokogiri (1.10.5)
154
+ nokogiri (1.10.9)
145
155
  mini_portile2 (~> 2.4.0)
156
+ octokit (4.16.0)
157
+ faraday (>= 0.9)
158
+ sawyer (~> 0.8.0, >= 0.5.3)
146
159
  os (1.0.1)
147
- parallel (1.19.0)
148
- parser (2.6.5.0)
160
+ parallel (1.19.1)
161
+ parser (2.7.0.4)
149
162
  ast (~> 2.4.0)
150
- public_suffix (4.0.1)
163
+ public_suffix (4.0.3)
151
164
  raabro (1.1.6)
152
- rack (2.0.7)
165
+ rack (2.2.2)
153
166
  rack-test (1.1.0)
154
167
  rack (>= 1.0, < 3)
155
168
  rails (6.0.0)
@@ -179,19 +192,20 @@ GEM
179
192
  rake (>= 0.8.7)
180
193
  thor (>= 0.20.3, < 2.0)
181
194
  rainbow (3.0.0)
182
- rake (10.5.0)
195
+ rake (13.0.1)
183
196
  redis (4.1.3)
197
+ retriable (3.1.2)
184
198
  rly (0.2.3)
185
199
  rspec (3.9.0)
186
200
  rspec-core (~> 3.9.0)
187
201
  rspec-expectations (~> 3.9.0)
188
202
  rspec-mocks (~> 3.9.0)
189
- rspec-core (3.9.0)
190
- rspec-support (~> 3.9.0)
203
+ rspec-core (3.9.1)
204
+ rspec-support (~> 3.9.1)
191
205
  rspec-expectations (3.9.0)
192
206
  diff-lcs (>= 1.2.0, < 2.0)
193
207
  rspec-support (~> 3.9.0)
194
- rspec-mocks (3.9.0)
208
+ rspec-mocks (3.9.1)
195
209
  diff-lcs (>= 1.2.0, < 2.0)
196
210
  rspec-support (~> 3.9.0)
197
211
  rspec-rails (3.9.0)
@@ -202,7 +216,7 @@ GEM
202
216
  rspec-expectations (~> 3.9.0)
203
217
  rspec-mocks (~> 3.9.0)
204
218
  rspec-support (~> 3.9.0)
205
- rspec-support (3.9.0)
219
+ rspec-support (3.9.2)
206
220
  rubocop (0.76.0)
207
221
  jaro_winkler (~> 1.5.1)
208
222
  parallel (~> 1.10)
@@ -210,13 +224,16 @@ GEM
210
224
  rainbow (>= 2.2.2, < 4.0)
211
225
  ruby-progressbar (~> 1.7)
212
226
  unicode-display_width (>= 1.4.0, < 1.7)
213
- rubocop-rspec (1.36.0)
227
+ rubocop-rspec (1.37.0)
214
228
  rubocop (>= 0.68.1)
215
229
  ruby-progressbar (1.10.1)
216
230
  safe_yaml (1.0.5)
217
- signet (0.12.0)
231
+ sawyer (0.8.2)
232
+ addressable (>= 2.3.5)
233
+ faraday (> 0.8, < 2.0)
234
+ signet (0.13.0)
218
235
  addressable (~> 2.3)
219
- faraday (~> 0.9)
236
+ faraday (>= 0.17.3, < 2.0)
220
237
  jwt (>= 1.5, < 3.0)
221
238
  multi_json (~> 1.10)
222
239
  sprockets (4.0.0)
@@ -226,21 +243,21 @@ GEM
226
243
  actionpack (>= 4.0)
227
244
  activesupport (>= 4.0)
228
245
  sprockets (>= 3.0.0)
229
- sqlite3 (1.4.1)
230
- thor (0.20.3)
246
+ sqlite3 (1.4.2)
247
+ thor (1.0.1)
231
248
  thread_safe (0.3.6)
232
249
  timecop (0.9.1)
233
- tzinfo (1.2.5)
250
+ tzinfo (1.2.6)
234
251
  thread_safe (~> 0.1)
235
- unicode-display_width (1.6.0)
236
- webmock (3.7.6)
252
+ unicode-display_width (1.6.1)
253
+ webmock (3.8.2)
237
254
  addressable (>= 2.3.6)
238
255
  crack (>= 0.3.2)
239
256
  hashdiff (>= 0.4.0, < 2.0.0)
240
257
  websocket-driver (0.7.1)
241
258
  websocket-extensions (>= 0.1.0)
242
259
  websocket-extensions (0.1.4)
243
- zeitwerk (2.2.1)
260
+ zeitwerk (2.3.0)
244
261
 
245
262
  PLATFORMS
246
263
  ruby
@@ -249,12 +266,13 @@ DEPENDENCIES
249
266
  appraisal
250
267
  bundler (~> 2.0)
251
268
  cloudtasker!
269
+ github_changelog_generator
252
270
  rails (= 6.0)
253
- rake (~> 10.0)
271
+ rake (>= 12.3.3)
254
272
  rspec (~> 3.0)
255
273
  rspec-rails
256
274
  rubocop (= 0.76.0)
257
- rubocop-rspec
275
+ rubocop-rspec (= 1.37.0)
258
276
  sqlite3
259
277
  timecop
260
278
  webmock
data/lib/cloudtasker.rb CHANGED
@@ -8,6 +8,7 @@ require 'cloudtasker/config'
8
8
  require 'cloudtasker/authentication_error'
9
9
  require 'cloudtasker/dead_worker_error'
10
10
  require 'cloudtasker/invalid_worker_error'
11
+ require 'cloudtasker/max_task_size_exceeded_error'
11
12
 
12
13
  require 'cloudtasker/middleware/chain'
13
14
  require 'cloudtasker/authenticator'
@@ -82,6 +82,29 @@ module Cloudtasker
82
82
  Google::Protobuf::Timestamp.new.tap { |e| e.seconds = schedule_time.to_i }
83
83
  end
84
84
 
85
+ #
86
+ # Format the job payload sent to Cloud Tasks.
87
+ #
88
+ # @param [Hash] hash The worker payload.
89
+ #
90
+ # @return [Hash] The Cloud Task payloadd.
91
+ #
92
+ def self.format_task_payload(payload)
93
+ payload = JSON.parse(payload.to_json, symbolize_names: true) # deep dup
94
+
95
+ # Format schedule time to Google Protobuf timestamp
96
+ payload[:schedule_time] = format_schedule_time(payload[:schedule_time])
97
+
98
+ # Encode job content to support UTF-8. Google Cloud Task
99
+ # expect content to be ASCII-8BIT compatible (binary)
100
+ payload[:http_request][:headers] ||= {}
101
+ payload[:http_request][:headers][Cloudtasker::Config::CONTENT_TYPE_HEADER] = 'text/json'
102
+ payload[:http_request][:headers][Cloudtasker::Config::ENCODING_HEADER] = 'Base64'
103
+ payload[:http_request][:body] = Base64.encode64(payload[:http_request][:body])
104
+
105
+ payload
106
+ end
107
+
85
108
  #
86
109
  # Find a task by id.
87
110
  #
@@ -104,10 +127,7 @@ module Cloudtasker
104
127
  # @return [Cloudtasker::Backend::GoogleCloudTask, nil] The created task.
105
128
  #
106
129
  def self.create(payload)
107
- # Format payload
108
- payload = payload.merge(
109
- schedule_time: format_schedule_time(payload[:schedule_time])
110
- ).compact
130
+ payload = format_task_payload(payload)
111
131
 
112
132
  # Extract relative queue name
113
133
  relative_queue = payload.delete(:queue)
@@ -126,7 +146,7 @@ module Cloudtasker
126
146
  #
127
147
  def self.delete(id)
128
148
  client.delete_task(id)
129
- rescue Google::Gax::RetryError
149
+ rescue Google::Gax::RetryError, GRPC::NotFound
130
150
  nil
131
151
  end
132
152
 
@@ -121,7 +121,7 @@ module Cloudtasker
121
121
  @http_request = http_request
122
122
  @schedule_time = Time.at(schedule_time || 0)
123
123
  @retries = retries || 0
124
- @queue = queue
124
+ @queue = queue || Cloudtasker::Config::DEFAULT_JOB_QUEUE
125
125
  end
126
126
 
127
127
  #
@@ -163,10 +163,13 @@ module Cloudtasker
163
163
  # @param [Integer] interval The delay in seconds before retrying the task
164
164
  #
165
165
  def retry_later(interval, is_error: true)
166
- redis.write(gid,
167
- retries: is_error ? retries + 1 : retries,
168
- http_request: http_request,
169
- schedule_time: (Time.now + interval).to_i)
166
+ redis.write(
167
+ gid,
168
+ retries: is_error ? retries + 1 : retries,
169
+ http_request: http_request,
170
+ schedule_time: (Time.now + interval).to_i,
171
+ queue: queue
172
+ )
170
173
  end
171
174
 
172
175
  #
@@ -48,6 +48,8 @@ module Cloudtasker
48
48
  # @return [Cloudtasker::CloudTask] The created task.
49
49
  #
50
50
  def self.create(payload)
51
+ raise MaxTaskSizeExceededError if payload.to_json.bytesize > Config::MAX_TASK_SIZE
52
+
51
53
  resp = backend.create(payload)&.to_h
52
54
  resp ? new(resp) : nil
53
55
  end
@@ -9,9 +9,21 @@ module Cloudtasker
9
9
  attr_writer :secret, :gcp_location_id, :gcp_project_id,
10
10
  :gcp_queue_prefix, :processor_path, :logger, :mode, :max_retries
11
11
 
12
+ # Max Cloud Task size in bytes
13
+ MAX_TASK_SIZE = 100 * 1024 # 100 KB
14
+
12
15
  # Retry header in Cloud Task responses
13
16
  RETRY_HEADER = 'X-CloudTasks-TaskExecutionCount'
14
17
 
18
+ # Content-Transfer-Encoding header in Cloud Task responses
19
+ ENCODING_HEADER = 'Content-Transfer-Encoding'
20
+
21
+ # Content Type
22
+ CONTENT_TYPE_HEADER = 'Content-Type'
23
+
24
+ # Authorization header
25
+ AUTHORIZATION_HEADER = 'Authorization'
26
+
15
27
  # Default values
16
28
  DEFAULT_LOCATION_ID = 'us-east1'
17
29
  DEFAULT_PROCESSOR_PATH = '/cloudtasker/run'
@@ -101,8 +113,14 @@ module Cloudtasker
101
113
  def processor_host=(val)
102
114
  @processor_host = val
103
115
 
116
+ # Check if Rails supports host filtering
117
+ return unless val &&
118
+ defined?(Rails) &&
119
+ Rails.application.config.respond_to?(:hosts) &&
120
+ Rails.application.config.hosts&.any?
121
+
104
122
  # Add processor host to the list of authorized hosts
105
- Rails.application.config.hosts << val.gsub(%r{https?://}, '') if val && defined?(Rails)
123
+ Rails.application.config.hosts << val.gsub(%r{https?://}, '')
106
124
  end
107
125
 
108
126
  #
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cloudtasker
4
+ # Handle Cloud Task size quota
5
+ # See: https://cloud.google.com/appengine/quotas#Task_Queue
6
+ #
7
+ class MaxTaskSizeExceededError < StandardError
8
+ MSG = 'The size of Cloud Tasks must not exceed 100KB'
9
+
10
+ def initialize(msg = MSG)
11
+ super
12
+ end
13
+ end
14
+ end
@@ -8,13 +8,17 @@ module Cloudtasker
8
8
  # Suffix added to cache keys when locking them
9
9
  LOCK_KEY_PREFIX = 'cloudtasker/lock'
10
10
 
11
+ def self.client
12
+ @client ||= Redis.new(Cloudtasker.config.redis || {})
13
+ end
14
+
11
15
  #
12
16
  # Return the underlying redis client.
13
17
  #
14
18
  # @return [Redis] The redis client.
15
19
  #
16
20
  def client
17
- @client ||= Redis.new(Cloudtasker.config.redis || {})
21
+ @client ||= self.class.client
18
22
  end
19
23
 
20
24
  #
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Cloudtasker
4
- VERSION = '0.8.0'
4
+ VERSION = '0.9.2'
5
5
  end
@@ -35,7 +35,7 @@ module Cloudtasker
35
35
  def self.from_hash(hash)
36
36
  # Symbolize metadata keys and stringify job arguments
37
37
  payload = JSON.parse(hash.to_json, symbolize_names: true)
38
- payload[:job_args] = JSON.parse(hash[:job_args].to_json)
38
+ payload[:job_args] = JSON.parse(payload[:job_args].to_json)
39
39
 
40
40
  # Extract worker parameters
41
41
  klass_name = payload&.dig(:worker)
@@ -42,8 +42,8 @@ module Cloudtasker
42
42
  http_method: 'POST',
43
43
  url: Cloudtasker.config.processor_url,
44
44
  headers: {
45
- 'Content-Type' => 'application/json',
46
- 'Authorization' => "Bearer #{Authenticator.verification_token}"
45
+ Cloudtasker::Config::CONTENT_TYPE_HEADER => 'application/json',
46
+ Cloudtasker::Config::AUTHORIZATION_HEADER => "Bearer #{Authenticator.verification_token}"
47
47
  },
48
48
  body: worker_payload.to_json
49
49
  },
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cloudtasker
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.9.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Arnaud Lachaume
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-11-27 00:00:00.000000000 Z
11
+ date: 2020-03-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -126,16 +126,16 @@ dependencies:
126
126
  name: rake
127
127
  requirement: !ruby/object:Gem::Requirement
128
128
  requirements:
129
- - - "~>"
129
+ - - ">="
130
130
  - !ruby/object:Gem::Version
131
- version: '10.0'
131
+ version: 12.3.3
132
132
  type: :development
133
133
  prerelease: false
134
134
  version_requirements: !ruby/object:Gem::Requirement
135
135
  requirements:
136
- - - "~>"
136
+ - - ">="
137
137
  - !ruby/object:Gem::Version
138
- version: '10.0'
138
+ version: 12.3.3
139
139
  - !ruby/object:Gem::Dependency
140
140
  name: rspec
141
141
  requirement: !ruby/object:Gem::Requirement
@@ -168,16 +168,16 @@ dependencies:
168
168
  name: rubocop-rspec
169
169
  requirement: !ruby/object:Gem::Requirement
170
170
  requirements:
171
- - - ">="
171
+ - - '='
172
172
  - !ruby/object:Gem::Version
173
- version: '0'
173
+ version: 1.37.0
174
174
  type: :development
175
175
  prerelease: false
176
176
  version_requirements: !ruby/object:Gem::Requirement
177
177
  requirements:
178
- - - ">="
178
+ - - '='
179
179
  - !ruby/object:Gem::Version
180
- version: '0'
180
+ version: 1.37.0
181
181
  - !ruby/object:Gem::Dependency
182
182
  name: timecop
183
183
  requirement: !ruby/object:Gem::Requirement
@@ -264,10 +264,10 @@ files:
264
264
  - CHANGELOG.md
265
265
  - CODE_OF_CONDUCT.md
266
266
  - Gemfile
267
- - Gemfile.lock
268
267
  - LICENSE.txt
269
268
  - README.md
270
269
  - Rakefile
270
+ - _config.yml
271
271
  - app/controllers/cloudtasker/application_controller.rb
272
272
  - app/controllers/cloudtasker/worker_controller.rb
273
273
  - bin/console
@@ -320,6 +320,7 @@ files:
320
320
  - lib/cloudtasker/engine.rb
321
321
  - lib/cloudtasker/invalid_worker_error.rb
322
322
  - lib/cloudtasker/local_server.rb
323
+ - lib/cloudtasker/max_task_size_exceeded_error.rb
323
324
  - lib/cloudtasker/meta_store.rb
324
325
  - lib/cloudtasker/middleware/chain.rb
325
326
  - lib/cloudtasker/redis_client.rb