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 +4 -4
- data/.gems +3 -3
- data/lib/tq.rb +250 -3
- data/lib/version.rb +1 -1
- data/test/helper.rb +37 -93
- data/test/suite.rb +1 -1
- data/test/test_auth.rb +7 -39
- data/test/test_logger.rb +34 -29
- data/test/test_run.rb +55 -186
- metadata +11 -15
- data/lib/tq/app.rb +0 -176
- data/lib/tq/queue.rb +0 -161
- data/lib/tq/shell.rb +0 -104
- data/test/test_shell.rb +0 -161
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6c715d2a759e261f57e2f4f3a745e8c641187e8e
|
4
|
+
data.tar.gz: a996d693740eb9a6846bc510e178e6c935639b41
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7c350e6cd8cf00d23926f4bb980af5f8221a6346ba2d3047480dd80ec0052fd81f3441fd0df7b83c26d12bdd6391bfaf8ce39e04a77094acb9c3e689856440c7
|
7
|
+
data.tar.gz: 44cbccddca8ed63e62e06e629b70c81598e43078df946f507e796821246d547f34fd92d414c8135db76c840ffcf59550ab82a6e043f0d5e7b1e906027f4b0aa6
|
data/.gems
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
google-api-client -v 0.
|
2
|
-
minitest -v 5.
|
3
|
-
parallel -v 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
|
data/lib/version.rb
CHANGED
data/test/helper.rb
CHANGED
@@ -4,7 +4,9 @@ require 'logger'
|
|
4
4
|
gem 'minitest'
|
5
5
|
require 'minitest/autorun'
|
6
6
|
|
7
|
-
require '
|
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::
|
19
|
+
Google::Apis.logger = logger
|
18
20
|
end
|
19
21
|
|
20
22
|
def current_logger
|
21
|
-
Google::
|
23
|
+
Google::Apis.logger
|
22
24
|
end
|
23
25
|
|
24
26
|
class QueueHelper
|
25
27
|
|
26
|
-
|
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
|
-
|
57
|
-
|
58
|
-
|
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
|
66
|
-
client
|
67
|
-
client.
|
68
|
-
|
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
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
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
|
90
|
-
end
|
91
|
-
|
92
|
-
def all_payloads!
|
93
|
-
map! { |t| decode(t['payloadBase64']) }
|
51
|
+
return tasks
|
94
52
|
end
|
95
53
|
|
96
|
-
def
|
97
|
-
|
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
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
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
|
-
|
124
|
-
|
67
|
+
client = CloudTasks::CloudTasksService.new
|
68
|
+
client.authorization = creds
|
69
|
+
client
|
125
70
|
end
|
126
71
|
|
127
|
-
def
|
128
|
-
|
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
|
|
data/test/suite.rb
CHANGED