cloudtasker 0.8.0 → 0.9.2

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