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