tq 0.2.1 → 0.3.1

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: 877e9730d05610f3b211c7a0d69817f60149bde4
4
- data.tar.gz: eb2d73a1f7e5a63659fe53041171bde055e59523
3
+ metadata.gz: 6c715d2a759e261f57e2f4f3a745e8c641187e8e
4
+ data.tar.gz: a996d693740eb9a6846bc510e178e6c935639b41
5
5
  SHA512:
6
- metadata.gz: 9c2539a74728b068c962eab6e1637559664e1a5a74514623893b756166c1246641c5b33dc5892e671ada06a7e2760bba2453137b0ba98f74813c729bbdbaed3b
7
- data.tar.gz: 2981fa8c831cf086873d2d9dfd2fef886533dc01baaa5521b0d4ea2f6f6a701ed37a165299f558a92ba3397d98ddab54415f1658bad433192335a261063b9b45
6
+ metadata.gz: 7c350e6cd8cf00d23926f4bb980af5f8221a6346ba2d3047480dd80ec0052fd81f3441fd0df7b83c26d12bdd6391bfaf8ce39e04a77094acb9c3e689856440c7
7
+ data.tar.gz: 44cbccddca8ed63e62e06e629b70c81598e43078df946f507e796821246d547f34fd92d414c8135db76c840ffcf59550ab82a6e043f0d5e7b1e906027f4b0aa6
data/.gems CHANGED
@@ -1,3 +1,3 @@
1
- google-api-client -v 0.8.4
2
- minitest -v 5.5.1
3
- parallel -v 1.4.1
1
+ google-api-client -v 0.19.3
2
+ minitest -v 5.11.1
3
+ parallel -v 1.12.1
data/lib/tq.rb CHANGED
@@ -1,5 +1,252 @@
1
+ require 'json'
2
+
3
+ require 'googleauth'
4
+ require 'google/apis'
5
+ require 'google/apis/cloudtasks_v2beta2'
6
+ require 'parallel'
7
+
1
8
  require_relative 'version'
2
- require_relative 'tq/app'
3
- require_relative 'tq/queue'
4
- require_relative 'tq/logger'
5
9
 
10
+ module TQ
11
+
12
+ CloudTasks = Google::Apis::CloudtasksV2beta2
13
+
14
+ API_SCOPES = ['https://www.googleapis.com/auth/cloud-tasks' ]
15
+
16
+ module Utils
17
+
18
+ def options_including(keys, options)
19
+ options.select { |k,_| keys.include?(k) }
20
+ end
21
+
22
+ end
23
+
24
+ class App
25
+
26
+ attr_reader :worker, :queue_in, :queue_out, :queue_err, :logger, :concurrency, :env
27
+ def initialize( worker, queue_in, queue_out: nil, queue_err: nil,
28
+ logger: nil, concurrency: 1, env: {} )
29
+ @worker = worker
30
+ @queue_in = queue_in
31
+ @queue_out = queue_out
32
+ @queue_err = queue_err
33
+ @logger = logger
34
+ @concurrency = concurrency
35
+ @env = env
36
+ end
37
+
38
+ def call(auth_file=nil)
39
+ if auth_file
40
+ run!( service_account_client(auth_file) )
41
+ else
42
+ run!( default_client )
43
+ end
44
+ end
45
+
46
+ def run!(client)
47
+ setup_api_logger!
48
+ qin = TQ::Queue.new(client, queue_in)
49
+ qout = queue_out.nil? ? nil : TQ::Queue.new(client, queue_out)
50
+ qerr = queue_err.nil? ? nil : TQ::Queue.new(client, queue_err)
51
+
52
+ tasks = qin.lease!
53
+
54
+ Parallel.each(tasks, :in_threads => concurrency) do |task|
55
+ worker.new(qin, qout, qerr, env_with_logger).call(task)
56
+ end
57
+ end
58
+
59
+ def default_client
60
+ CloudTasks::CloudTasksService.new
61
+ end
62
+
63
+ def service_account_client(file)
64
+ return TQ::ServiceAccount.client(file)
65
+ end
66
+
67
+ def setup_api_logger!
68
+ Google::Apis.logger = logger
69
+ end
70
+
71
+ def env_with_logger
72
+ env.merge({ 'logger' => logger })
73
+ end
74
+
75
+ end
76
+
77
+ module ServiceAccount
78
+ extend self
79
+
80
+ def client(file)
81
+ creds = Google::Auth::ServiceAccountCredentials.make_creds(
82
+ :json_key_io => File.open(file, 'r'),
83
+ :scope => TQ::API_SCOPES
84
+ )
85
+ creds.fetch_access_token!
86
+
87
+ client = CloudTasks::CloudTasksService.new
88
+ client.authorization = creds
89
+ client
90
+ end
91
+
92
+ end
93
+
94
+ class QueueSpec
95
+ extend TQ::Utils
96
+
97
+ def self.from_hash(data)
98
+ return new(
99
+ data['project'],
100
+ data['location'],
101
+ data['name'],
102
+ **options_including(['lease_duration','max_tasks'], data)
103
+ )
104
+ end
105
+
106
+ attr_reader :project, :location, :name, :lease_duration, :max_tasks
107
+ def initialize(project, location, name,
108
+ lease_duration: '60s', max_tasks: 1)
109
+ @project = project
110
+ @location = location
111
+ @name = name
112
+ @lease_duration = lease_duration
113
+ @max_tasks = max_tasks
114
+ end
115
+
116
+ def queue_name
117
+ "projects/#{project}/locations/#{location}/queues/#{name}"
118
+ end
119
+
120
+ end
121
+
122
+
123
+ class Queue
124
+
125
+ attr_reader :client, :queue
126
+ def initialize(client, spec)
127
+ @client = client
128
+ @queue = spec
129
+ end
130
+
131
+ def queue_name
132
+ queue.queue_name
133
+ end
134
+
135
+ def lease!
136
+ msg = { :lease_duration => queue.lease_duration,
137
+ :max_tasks => queue.max_tasks,
138
+ :response_view => 'FULL'
139
+ }
140
+ results = client.lease_tasks( queue_name,
141
+ CloudTasks::LeaseTasksRequest.new( **msg )
142
+ )
143
+ items = results.tasks || []
144
+ items.map {|t| new_task(t)}
145
+ end
146
+
147
+
148
+ # Note: does not actually extend the time, but sets the total duration
149
+ # to the given amount (minus any already elapsed time).
150
+ #
151
+ def extend!(task, dur)
152
+ msg = { :lease_duration => dur,
153
+ :schedule_time => task.schedule_time,
154
+ :response_view => 'FULL'
155
+ }
156
+ results = client.renew_task_lease( task.name,
157
+ CloudTasks::RenewLeaseRequest.new( **msg )
158
+ )
159
+ return new_task(results)
160
+ end
161
+
162
+ def push!(payload, tag=nil)
163
+ msg = { :payload => encode(payload), :tag => tag }.reject {|k,v| v.nil?}
164
+ results = client.create_task( queue_name,
165
+ CloudTasks::CreateTaskRequest.new(
166
+ :task => CloudTasks::Task.new(
167
+ :pull_message => CloudTasks::PullMessage.new( **msg )
168
+ )
169
+ )
170
+ )
171
+ new_task(results)
172
+ end
173
+
174
+ def finish!(task)
175
+ results = client.acknowledge_task( task.name,
176
+ CloudTasks::AcknowledgeTaskRequest.new( :schedule_time => task.schedule_time )
177
+ )
178
+ return
179
+ end
180
+
181
+
182
+ private
183
+
184
+ def new_task(t)
185
+ TQ::Task.new self, t
186
+ end
187
+
188
+ def encode(obj)
189
+ JSON.dump(obj)
190
+ end
191
+
192
+ end
193
+
194
+ class Task
195
+
196
+ attr_reader :task
197
+ def initialize(queue, task)
198
+ @queue = queue
199
+ @task = task
200
+ @clock = Time
201
+ end
202
+
203
+ def name
204
+ self.task.name
205
+ end
206
+
207
+ def expires
208
+ DateTime.rfc3339(self.task.schedule_time).to_time
209
+ end
210
+
211
+ def tag
212
+ self.task.tag
213
+ end
214
+
215
+ def payload
216
+ decode self.task.pull_message.payload
217
+ end
218
+
219
+ def finish!
220
+ @queue.finish!(self.task)
221
+ end
222
+
223
+ def extend!(dur)
224
+ @queue.extend!(self.task, dur)
225
+ end
226
+
227
+ def clock!(_)
228
+ @clock = _; return self
229
+ end
230
+
231
+ def reset_clock!
232
+ @clock = Time; return self
233
+ end
234
+
235
+ def lease_remaining
236
+ self.expires - @clock.now
237
+ end
238
+
239
+ def lease_expired?
240
+ self.expires < @clock.now
241
+ end
242
+
243
+ private
244
+
245
+ def decode(str)
246
+ JSON.load(str)
247
+ end
248
+
249
+ end
250
+
251
+
252
+ end
@@ -1,4 +1,4 @@
1
1
 
2
2
  module TQ
3
- VERSION = '0.2.1'
3
+ VERSION = '0.3.1'
4
4
  end
@@ -4,7 +4,9 @@ require 'logger'
4
4
  gem 'minitest'
5
5
  require 'minitest/autorun'
6
6
 
7
- require 'google/api_client'
7
+ require 'googleauth'
8
+ require 'google/apis'
9
+ require 'google/apis/cloudtasks_v2beta2'
8
10
 
9
11
  module TestUtils
10
12
  extend self
@@ -14,122 +16,64 @@ module TestUtils
14
16
  FileUtils.mkdir_p( File.dirname(file) )
15
17
  logger = Logger.new( File.open(file, 'w' ) )
16
18
  logger.level = ENV['DEBUG'] ? Logger::DEBUG : Logger::INFO
17
- Google::APIClient.logger = logger
19
+ Google::Apis.logger = logger
18
20
  end
19
21
 
20
22
  def current_logger
21
- Google::APIClient.logger
23
+ Google::Apis.logger
22
24
  end
23
25
 
24
26
  class QueueHelper
25
27
 
26
- attr_reader :project, :queue
27
- def initialize(project,queue)
28
- @project, @queue = project, queue
29
- @service_auth = false
30
- end
31
-
32
- def auth_files(secrets,creds)
33
- @secrets_file, @creds_file = secrets, creds
34
- @service_auth = false
35
- return self
36
- end
37
-
38
- def service_auth_files(issuer,p12)
39
- @secrets_issuer, @secrets_p12 = issuer, p12
40
- @service_auth = true
41
- return self
42
- end
43
-
44
- def authorized_client
45
- @authorized_client ||= (
46
- if @service_auth
47
- TQ::App.new('test_app',nil).service_auth!(
48
- File.read(@secrets_issuer).chomp, @secrets_p12
49
- )
50
- else
51
- TQ::App.new('test_app',nil).auth!(@secrets_file, @creds_file)
52
- end
53
- )
54
- end
28
+ CloudTasks = Google::Apis::CloudtasksV2beta2
55
29
 
56
- # Note: inaccurate, don't use
57
- def peek()
58
- client, api = authorized_client
59
- results = client.execute!( :api_method => api.tasks.list,
60
- :parameters => { :project => project, :taskqueue => queue }
61
- )
62
- items = results.data['items'] || []
30
+ def initialize(spec, authfile)
31
+ @queue = spec
32
+ @authfile = authfile
63
33
  end
64
34
 
65
- def push!(payload)
66
- client, api = authorized_client
67
- client.execute!( :api_method => api.tasks.insert,
68
- :parameters => { :project => project, :taskqueue => queue },
69
- :body_object => {
70
- 'queueName' => queue,
71
- 'payloadBase64' => encode(payload)
72
- }
35
+ def purge!
36
+ client = service_account_client
37
+ client.purge_queue(@queue.queue_name,
38
+ CloudTasks::PurgeQueueRequest.new
73
39
  )
40
+ return
74
41
  end
75
42
 
76
- def pop!(n=1)
77
- client, api = authorized_client
78
- results = client.execute!( :api_method => api.tasks.lease,
79
- :parameters => { :project => project, :taskqueue => queue,
80
- :leaseSecs => 60, :numTasks => n
81
- }
82
- )
83
- items = results.data['items'] || []
84
- items.each do |item|
85
- client.execute!( :api_method => api.tasks.delete,
86
- :parameters => { :project => project, :taskqueue => queue, :task => item['id'] }
87
- )
43
+ # Note: longer way to do it, but gets the count
44
+ def clear!
45
+ client = service_account_client
46
+ results = client.list_project_location_queue_tasks(@queue.queue_name)
47
+ tasks = (results.tasks || [])
48
+ tasks.each do |t|
49
+ client.delete_project_location_queue_task( t.name )
88
50
  end
89
- return items
90
- end
91
-
92
- def all_payloads!
93
- map! { |t| decode(t['payloadBase64']) }
51
+ return tasks
94
52
  end
95
53
 
96
- def all_tags!
97
- map! { |t| t['tag'] }
54
+ def push!(data)
55
+ client = service_account_client
56
+ q = TQ::Queue.new(client, @queue)
57
+ return q.push!(data)
98
58
  end
99
59
 
100
- def all_payloads_and_tags!
101
- map! { |t| {:payload => decode(t['payloadBase64']),
102
- :tag => t['tag']
103
- }
104
- }
105
- end
106
-
107
- def map!(&b)
108
- clear!.map(&b)
109
- end
110
-
111
- def clear!
112
- client, api = authorized_client
113
- done = false
114
- all = []
115
- while !done do
116
- batch = pop!(10)
117
- done = batch.empty? || batch.length < 10
118
- all = all + batch
119
- end
120
- all
121
- end
60
+ def service_account_client
61
+ creds = Google::Auth::ServiceAccountCredentials.make_creds(
62
+ :json_key_io => File.open(@authfile, 'r'),
63
+ :scope => TQ::API_SCOPES
64
+ )
65
+ creds.fetch_access_token!
122
66
 
123
- def encode(obj)
124
- Base64.urlsafe_encode64(JSON.dump(obj))
67
+ client = CloudTasks::CloudTasksService.new
68
+ client.authorization = creds
69
+ client
125
70
  end
126
71
 
127
- def decode(str)
128
- JSON.load(Base64.urlsafe_decode64(str))
72
+ def messages!
73
+ clear!.map {|t| t.pull_message }
129
74
  end
130
75
 
131
76
  end
132
77
 
133
-
134
78
  end
135
79
 
@@ -1,4 +1,4 @@
1
1
 
2
- %w[ test_shell test_run test_logger ].each do |rb|
2
+ %w[ test_auth test_run test_logger ].each do |rb|
3
3
  require_relative rb
4
4
  end