hastci 0.1.0 → 0.1.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.
- checksums.yaml +4 -4
- data/devenv.lock +10 -10
- data/lib/hastci/api_client.rb +33 -20
- data/lib/hastci/cli.rb +40 -17
- data/lib/hastci/config.rb +1 -1
- data/lib/hastci/heartbeat.rb +3 -2
- data/lib/hastci/session.rb +2 -1
- data/lib/hastci/version.rb +1 -1
- data/spec/pacts/hastci_rspec-hastci_api.json +133 -26
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b5b2325eb3310d82c948100ad8944303a1cafc0f3824497fa68ff3a2d229184b
|
|
4
|
+
data.tar.gz: 93b8a6e612a613d8491624008fa0e334653aff2b769e437385141c56ad62372b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d8d05f307a3408f5238081361f11c5fafbf5e5f1c1eb822bbeaf8dd42fb172bc4048ca7ae6d3303aa5cf77de543f51cea829313e7efaee814c74df59680debe3
|
|
7
|
+
data.tar.gz: 9b557ef0c58252027907cb79197d00273e100a7735b2072203d68c897c6a966cb0b47d0d985993ebcedf2954ab1238cdab9333902352a28cdb8fd3344d0a5397
|
data/devenv.lock
CHANGED
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
"devenv": {
|
|
4
4
|
"locked": {
|
|
5
5
|
"dir": "src/modules",
|
|
6
|
-
"lastModified":
|
|
6
|
+
"lastModified": 1766921574,
|
|
7
7
|
"owner": "cachix",
|
|
8
8
|
"repo": "devenv",
|
|
9
|
-
"rev": "
|
|
9
|
+
"rev": "02c9dcf3e050400d8101057f9f00ec458af7c959",
|
|
10
10
|
"type": "github"
|
|
11
11
|
},
|
|
12
12
|
"original": {
|
|
@@ -19,10 +19,10 @@
|
|
|
19
19
|
"flake-compat": {
|
|
20
20
|
"flake": false,
|
|
21
21
|
"locked": {
|
|
22
|
-
"lastModified":
|
|
22
|
+
"lastModified": 1766929376,
|
|
23
23
|
"owner": "edolstra",
|
|
24
24
|
"repo": "flake-compat",
|
|
25
|
-
"rev": "
|
|
25
|
+
"rev": "236f248441a986331cda53b039e7f9fd96e03635",
|
|
26
26
|
"type": "github"
|
|
27
27
|
},
|
|
28
28
|
"original": {
|
|
@@ -34,10 +34,10 @@
|
|
|
34
34
|
"flake-compat_2": {
|
|
35
35
|
"flake": false,
|
|
36
36
|
"locked": {
|
|
37
|
-
"lastModified":
|
|
37
|
+
"lastModified": 1766929376,
|
|
38
38
|
"owner": "edolstra",
|
|
39
39
|
"repo": "flake-compat",
|
|
40
|
-
"rev": "
|
|
40
|
+
"rev": "236f248441a986331cda53b039e7f9fd96e03635",
|
|
41
41
|
"type": "github"
|
|
42
42
|
},
|
|
43
43
|
"original": {
|
|
@@ -72,10 +72,10 @@
|
|
|
72
72
|
]
|
|
73
73
|
},
|
|
74
74
|
"locked": {
|
|
75
|
-
"lastModified":
|
|
75
|
+
"lastModified": 1765911976,
|
|
76
76
|
"owner": "cachix",
|
|
77
77
|
"repo": "git-hooks.nix",
|
|
78
|
-
"rev": "
|
|
78
|
+
"rev": "b68b780b69702a090c8bb1b973bab13756cc7a27",
|
|
79
79
|
"type": "github"
|
|
80
80
|
},
|
|
81
81
|
"original": {
|
|
@@ -128,10 +128,10 @@
|
|
|
128
128
|
]
|
|
129
129
|
},
|
|
130
130
|
"locked": {
|
|
131
|
-
"lastModified":
|
|
131
|
+
"lastModified": 1766728475,
|
|
132
132
|
"owner": "bobvanderlinden",
|
|
133
133
|
"repo": "nixpkgs-ruby",
|
|
134
|
-
"rev": "
|
|
134
|
+
"rev": "f167828eab19c3a7e3faa066a140d92e307f7b16",
|
|
135
135
|
"type": "github"
|
|
136
136
|
},
|
|
137
137
|
"original": {
|
data/lib/hastci/api_client.rb
CHANGED
|
@@ -18,13 +18,15 @@ module HastCI
|
|
|
18
18
|
private_constant :DEFAULT_MAX_RETRIES, :DEFAULT_INITIAL_BACKOFF, :DEFAULT_MAX_BACKOFF,
|
|
19
19
|
:DEFAULT_KEEP_ALIVE_TIMEOUT, :DEFAULT_OPEN_TIMEOUT, :DEFAULT_READ_TIMEOUT, :DEFAULT_WRITE_TIMEOUT
|
|
20
20
|
|
|
21
|
+
API_PATH_PREFIX = "/api/v1"
|
|
22
|
+
|
|
21
23
|
# Separate connections allow concurrent operations without blocking.
|
|
22
24
|
# E.g., heartbeat can continue while the main thread waits on a claim.
|
|
23
25
|
CONNECTION_DEFAULT = :default
|
|
24
26
|
CONNECTION_HEARTBEAT = :heartbeat
|
|
25
27
|
CONNECTION_ACK = :ack
|
|
26
28
|
|
|
27
|
-
private_constant :CONNECTION_DEFAULT, :CONNECTION_HEARTBEAT, :CONNECTION_ACK
|
|
29
|
+
private_constant :API_PATH_PREFIX, :CONNECTION_DEFAULT, :CONNECTION_HEARTBEAT, :CONNECTION_ACK
|
|
28
30
|
|
|
29
31
|
def initialize(config:, sleeper: Kernel.method(:sleep), max_retries: nil, random: Random.new)
|
|
30
32
|
@config = config
|
|
@@ -56,52 +58,54 @@ module HastCI
|
|
|
56
58
|
commit_sha: @config.commit_sha
|
|
57
59
|
)
|
|
58
60
|
response = ensure_hash_response(
|
|
59
|
-
post_json("/runs
|
|
61
|
+
post_json("#{API_PATH_PREFIX}/runs", {
|
|
60
62
|
run_key: run_key,
|
|
61
63
|
worker_id: worker_id,
|
|
62
64
|
commit_sha: commit_sha
|
|
63
65
|
}),
|
|
64
|
-
context: "/runs
|
|
66
|
+
context: "#{API_PATH_PREFIX}/runs"
|
|
65
67
|
)
|
|
66
68
|
|
|
67
69
|
{
|
|
68
|
-
run_id: fetch_required(response, "run_id", context: "/runs
|
|
69
|
-
status: fetch_required(response, "status", context: "/runs
|
|
70
|
-
role: fetch_required(response, "role", context: "/runs
|
|
70
|
+
run_id: fetch_required(response, "run_id", context: "#{API_PATH_PREFIX}/runs"),
|
|
71
|
+
status: fetch_required(response, "status", context: "#{API_PATH_PREFIX}/runs").to_sym,
|
|
72
|
+
role: fetch_required(response, "role", context: "#{API_PATH_PREFIX}/runs").to_sym
|
|
71
73
|
}
|
|
72
74
|
end
|
|
73
75
|
|
|
74
76
|
def seed(run_id:, tasks:)
|
|
75
|
-
post_json("/runs/#{run_id}/seed", {
|
|
77
|
+
post_json("#{API_PATH_PREFIX}/runs/#{run_id}/seed", {
|
|
76
78
|
tasks: tasks.map { |name| {name: name} }
|
|
77
79
|
})
|
|
78
80
|
end
|
|
79
81
|
|
|
80
82
|
def run_status(run_id:)
|
|
81
|
-
response = ensure_hash_response(
|
|
83
|
+
response = ensure_hash_response(
|
|
84
|
+
get_json("#{API_PATH_PREFIX}/runs/#{run_id}"),
|
|
85
|
+
context: "#{API_PATH_PREFIX}/runs/:run_id"
|
|
86
|
+
)
|
|
82
87
|
|
|
83
88
|
{
|
|
84
|
-
status: fetch_required(response, "status", context: "/runs
|
|
89
|
+
status: fetch_required(response, "status", context: "#{API_PATH_PREFIX}/runs/:run_id").to_sym
|
|
85
90
|
}
|
|
86
91
|
end
|
|
87
92
|
|
|
88
93
|
def claim(
|
|
89
|
-
|
|
94
|
+
run_id:,
|
|
90
95
|
worker_id: @config.worker_id,
|
|
91
96
|
batch: @config.claim_batch_size
|
|
92
97
|
)
|
|
93
98
|
response = ensure_hash_response(
|
|
94
|
-
post_json("/
|
|
95
|
-
run_key: run_key,
|
|
99
|
+
post_json("#{API_PATH_PREFIX}/runs/#{run_id}/claims?batch=#{batch}", {
|
|
96
100
|
worker_id: worker_id
|
|
97
101
|
}),
|
|
98
|
-
context: "/
|
|
102
|
+
context: "#{API_PATH_PREFIX}/runs/:run_id/claims"
|
|
99
103
|
)
|
|
100
104
|
|
|
101
105
|
tasks = Array(response.fetch("tasks", [])).map do |task_data|
|
|
102
106
|
Task.new(
|
|
103
|
-
id: fetch_required(task_data, "id", context: "/
|
|
104
|
-
name: fetch_required(task_data, "name", context: "/
|
|
107
|
+
id: fetch_required(task_data, "id", context: "#{API_PATH_PREFIX}/runs/:run_id/claims"),
|
|
108
|
+
name: fetch_required(task_data, "name", context: "#{API_PATH_PREFIX}/runs/:run_id/claims")
|
|
105
109
|
)
|
|
106
110
|
end
|
|
107
111
|
|
|
@@ -114,7 +118,7 @@ module HastCI
|
|
|
114
118
|
end
|
|
115
119
|
|
|
116
120
|
def ack(task_id:, status:, duration_s:, logs:)
|
|
117
|
-
post_json("/tasks/#{task_id}/
|
|
121
|
+
post_json("#{API_PATH_PREFIX}/tasks/#{task_id}/acknowledgment", {
|
|
118
122
|
status: status.to_s,
|
|
119
123
|
duration_s: duration_s,
|
|
120
124
|
logs: logs
|
|
@@ -122,11 +126,10 @@ module HastCI
|
|
|
122
126
|
end
|
|
123
127
|
|
|
124
128
|
def heartbeat(
|
|
125
|
-
|
|
129
|
+
run_id:,
|
|
126
130
|
worker_id: @config.worker_id
|
|
127
131
|
)
|
|
128
|
-
post_json("/
|
|
129
|
-
run_key: run_key,
|
|
132
|
+
post_json("#{API_PATH_PREFIX}/runs/#{run_id}/heartbeats", {
|
|
130
133
|
worker_id: worker_id
|
|
131
134
|
}, pool: CONNECTION_HEARTBEAT)
|
|
132
135
|
|
|
@@ -176,12 +179,13 @@ module HastCI
|
|
|
176
179
|
|
|
177
180
|
elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time if @config.debug
|
|
178
181
|
log_response(response, request_id, elapsed) if @config.debug
|
|
182
|
+
log_error_body(response, request_id) if @config.debug && response.code.to_i >= 400
|
|
179
183
|
parse_response(response)
|
|
180
184
|
end
|
|
181
185
|
end
|
|
182
186
|
|
|
183
187
|
def log_request(request, request_id)
|
|
184
|
-
HastCI.logger.debug("[#{request_id}] #{request.method} #{request.path}")
|
|
188
|
+
HastCI.logger.debug("[#{request_id}] #{request.method} #{@base_url}#{request.path}")
|
|
185
189
|
return unless request.body
|
|
186
190
|
|
|
187
191
|
body_preview = if request.body.length > 200
|
|
@@ -196,6 +200,15 @@ module HastCI
|
|
|
196
200
|
HastCI.logger.debug("[#{request_id}] Response: #{response.code} (#{(elapsed_s * 1000).round}ms)")
|
|
197
201
|
end
|
|
198
202
|
|
|
203
|
+
def log_error_body(response, request_id)
|
|
204
|
+
body = response.body.to_s
|
|
205
|
+
return if body.strip.empty?
|
|
206
|
+
|
|
207
|
+
summary = body.match(/No route matches[^<]*/)&.to_s
|
|
208
|
+
body_preview = summary || ((body.length > 200) ? "#{body[0, 200]}... (#{body.length} bytes)" : body)
|
|
209
|
+
HastCI.logger.debug("[#{request_id}] Error body: #{body_preview}")
|
|
210
|
+
end
|
|
211
|
+
|
|
199
212
|
def log_retry(request_id, attempt)
|
|
200
213
|
HastCI.logger.debug("[#{request_id}] Retry attempt #{attempt}")
|
|
201
214
|
end
|
data/lib/hastci/cli.rb
CHANGED
|
@@ -9,16 +9,22 @@ module HastCI
|
|
|
9
9
|
runner_exit_code = ExitCodes::SUCCESS
|
|
10
10
|
interrupted = false
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
setup_interrupt_handler(session, err) { interrupted = true }
|
|
12
|
+
interrupt_cleanup = nil
|
|
14
13
|
|
|
15
|
-
|
|
14
|
+
begin
|
|
15
|
+
cleanup_result = HastCI::Session.run(config: config) do |session|
|
|
16
|
+
interrupt_cleanup = setup_interrupt_handler(session, err) { interrupted = true }
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
session.start!
|
|
19
|
+
|
|
20
|
+
runner_exit_code = if session.stopping?
|
|
21
|
+
ExitCodes::SUCCESS
|
|
22
|
+
else
|
|
23
|
+
runner_block.call(session, argv, err, out)
|
|
24
|
+
end
|
|
21
25
|
end
|
|
26
|
+
ensure
|
|
27
|
+
interrupt_cleanup&.call
|
|
22
28
|
end
|
|
23
29
|
|
|
24
30
|
print_errors(cleanup_result, err: err)
|
|
@@ -81,20 +87,37 @@ module HastCI
|
|
|
81
87
|
|
|
82
88
|
# :nocov:
|
|
83
89
|
def self.setup_interrupt_handler(session, err, &on_interrupt)
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
90
|
+
signals = Queue.new
|
|
91
|
+
signal_count = 0
|
|
92
|
+
|
|
93
|
+
trap("INT") { signals << "SIGINT" }
|
|
94
|
+
trap("TERM") { signals << "SIGTERM" }
|
|
95
|
+
|
|
96
|
+
watcher = Thread.new do
|
|
97
|
+
loop do
|
|
98
|
+
signal = signals.pop
|
|
99
|
+
break if signal == :shutdown
|
|
100
|
+
|
|
101
|
+
already_stopping = session.stopping?
|
|
102
|
+
signal_count += 1
|
|
103
|
+
|
|
104
|
+
if already_stopping || signal_count > 1
|
|
105
|
+
err.puts("\nForce quit!")
|
|
106
|
+
exit!(1)
|
|
107
|
+
else
|
|
108
|
+
on_interrupt&.call
|
|
109
|
+
session.request_stop!(:user_interrupt)
|
|
110
|
+
err.puts("\nReceived #{signal} - shutting down after current task...")
|
|
111
|
+
end
|
|
92
112
|
end
|
|
93
113
|
end
|
|
94
114
|
|
|
95
|
-
|
|
96
|
-
|
|
115
|
+
lambda do
|
|
116
|
+
signals << :shutdown
|
|
117
|
+
watcher.join
|
|
118
|
+
end
|
|
97
119
|
end
|
|
120
|
+
|
|
98
121
|
private_class_method :setup_interrupt_handler
|
|
99
122
|
# :nocov:
|
|
100
123
|
end
|
data/lib/hastci/config.rb
CHANGED
data/lib/hastci/heartbeat.rb
CHANGED
|
@@ -7,8 +7,9 @@ module HastCI
|
|
|
7
7
|
|
|
8
8
|
private_constant :SHUTDOWN_TIMEOUT, :MAX_CONSECUTIVE_FAILURES
|
|
9
9
|
|
|
10
|
-
def initialize(api_client:, error_collector: nil, interval: 15)
|
|
10
|
+
def initialize(api_client:, run_id:, error_collector: nil, interval: 15)
|
|
11
11
|
@api_client = api_client
|
|
12
|
+
@run_id = run_id
|
|
12
13
|
@error_collector = error_collector
|
|
13
14
|
@interval = interval
|
|
14
15
|
@running = false
|
|
@@ -64,7 +65,7 @@ module HastCI
|
|
|
64
65
|
end
|
|
65
66
|
|
|
66
67
|
def send_heartbeat
|
|
67
|
-
@api_client.heartbeat
|
|
68
|
+
@api_client.heartbeat(run_id: @run_id)
|
|
68
69
|
@consecutive_failures = 0
|
|
69
70
|
rescue RetryExhaustedError, FatalApiError => e
|
|
70
71
|
@error_collector&.report(e)
|
data/lib/hastci/session.rb
CHANGED
|
@@ -203,6 +203,7 @@ module HastCI
|
|
|
203
203
|
|
|
204
204
|
@heartbeat = Heartbeat.new(
|
|
205
205
|
api_client: @api_client,
|
|
206
|
+
run_id: @run_id,
|
|
206
207
|
interval: @config.heartbeat_interval,
|
|
207
208
|
error_collector: @error_collector
|
|
208
209
|
)
|
|
@@ -233,7 +234,7 @@ module HastCI
|
|
|
233
234
|
max_size: @buffer_max_size,
|
|
234
235
|
fetcher: lambda { |limit|
|
|
235
236
|
batch_size = [limit, @config.claim_batch_size].min
|
|
236
|
-
@api_client.claim(batch: batch_size)
|
|
237
|
+
@api_client.claim(run_id: @run_id, batch: batch_size)
|
|
237
238
|
},
|
|
238
239
|
error_collector: @error_collector,
|
|
239
240
|
poll_interval: @poll_interval,
|
data/lib/hastci/version.rb
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"providerState": "a valid API key",
|
|
12
12
|
"request": {
|
|
13
13
|
"method": "post",
|
|
14
|
-
"path": "/runs
|
|
14
|
+
"path": "/api/v1/runs",
|
|
15
15
|
"headers": {
|
|
16
16
|
"Authorization": "Bearer test-api-key",
|
|
17
17
|
"Content-Type": "application/json"
|
|
@@ -39,13 +39,14 @@
|
|
|
39
39
|
"Content-Type": "application/json"
|
|
40
40
|
},
|
|
41
41
|
"body": {
|
|
42
|
-
"run_id": "
|
|
42
|
+
"run_id": "abc123def456",
|
|
43
43
|
"status": "seeding",
|
|
44
44
|
"role": "seeder"
|
|
45
45
|
},
|
|
46
46
|
"matchingRules": {
|
|
47
47
|
"$.body.run_id": {
|
|
48
|
-
"match": "
|
|
48
|
+
"match": "regex",
|
|
49
|
+
"regex": "^[0-9A-Za-z_-]{12}$"
|
|
49
50
|
},
|
|
50
51
|
"$.body.status": {
|
|
51
52
|
"match": "regex",
|
|
@@ -63,7 +64,7 @@
|
|
|
63
64
|
"providerState": "an invalid API key",
|
|
64
65
|
"request": {
|
|
65
66
|
"method": "post",
|
|
66
|
-
"path": "/runs
|
|
67
|
+
"path": "/api/v1/runs",
|
|
67
68
|
"headers": {
|
|
68
69
|
"Authorization": "Bearer invalid-api-key",
|
|
69
70
|
"Content-Type": "application/json"
|
|
@@ -91,7 +92,10 @@
|
|
|
91
92
|
"Content-Type": "application/json"
|
|
92
93
|
},
|
|
93
94
|
"body": {
|
|
94
|
-
"error":
|
|
95
|
+
"error": {
|
|
96
|
+
"code": "unauthorized",
|
|
97
|
+
"message": "Unauthorized"
|
|
98
|
+
}
|
|
95
99
|
},
|
|
96
100
|
"matchingRules": {
|
|
97
101
|
"$.body.error": {
|
|
@@ -105,9 +109,15 @@
|
|
|
105
109
|
"providerState": "a run exists",
|
|
106
110
|
"request": {
|
|
107
111
|
"method": "get",
|
|
108
|
-
"path": "/runs/
|
|
112
|
+
"path": "/api/v1/runs/abc123def456",
|
|
109
113
|
"headers": {
|
|
110
114
|
"Authorization": "Bearer test-api-key"
|
|
115
|
+
},
|
|
116
|
+
"matchingRules": {
|
|
117
|
+
"$.path": {
|
|
118
|
+
"match": "regex",
|
|
119
|
+
"regex": "^\\/api\\/v1\\/runs\\/[0-9A-Za-z_-]{12}$"
|
|
120
|
+
}
|
|
111
121
|
}
|
|
112
122
|
},
|
|
113
123
|
"response": {
|
|
@@ -131,7 +141,7 @@
|
|
|
131
141
|
"providerState": "a run exists",
|
|
132
142
|
"request": {
|
|
133
143
|
"method": "post",
|
|
134
|
-
"path": "/runs/
|
|
144
|
+
"path": "/api/v1/runs/abc123def456/seed",
|
|
135
145
|
"headers": {
|
|
136
146
|
"Authorization": "Bearer test-api-key",
|
|
137
147
|
"Content-Type": "application/json"
|
|
@@ -145,6 +155,12 @@
|
|
|
145
155
|
"name": "spec/models/post_spec.rb"
|
|
146
156
|
}
|
|
147
157
|
]
|
|
158
|
+
},
|
|
159
|
+
"matchingRules": {
|
|
160
|
+
"$.path": {
|
|
161
|
+
"match": "regex",
|
|
162
|
+
"regex": "^\\/api\\/v1\\/runs\\/[0-9A-Za-z_-]{12}\\/seed$"
|
|
163
|
+
}
|
|
148
164
|
}
|
|
149
165
|
},
|
|
150
166
|
"response": {
|
|
@@ -163,15 +179,20 @@
|
|
|
163
179
|
"providerState": "tasks are available",
|
|
164
180
|
"request": {
|
|
165
181
|
"method": "post",
|
|
166
|
-
"path": "/
|
|
182
|
+
"path": "/api/v1/runs/abc123def456/claims",
|
|
167
183
|
"query": "batch=10",
|
|
168
184
|
"headers": {
|
|
169
185
|
"Authorization": "Bearer test-api-key",
|
|
170
186
|
"Content-Type": "application/json"
|
|
171
187
|
},
|
|
172
188
|
"body": {
|
|
173
|
-
"run_key": "test-run-key",
|
|
174
189
|
"worker_id": "worker-0"
|
|
190
|
+
},
|
|
191
|
+
"matchingRules": {
|
|
192
|
+
"$.path": {
|
|
193
|
+
"match": "regex",
|
|
194
|
+
"regex": "^\\/api\\/v1\\/runs\\/[0-9A-Za-z_-]{12}\\/claims$"
|
|
195
|
+
}
|
|
175
196
|
}
|
|
176
197
|
},
|
|
177
198
|
"response": {
|
|
@@ -182,19 +203,27 @@
|
|
|
182
203
|
"body": {
|
|
183
204
|
"tasks": [
|
|
184
205
|
{
|
|
185
|
-
"id":
|
|
206
|
+
"id": 101,
|
|
186
207
|
"name": "spec/models/user_spec.rb"
|
|
187
208
|
},
|
|
188
209
|
{
|
|
189
|
-
"id":
|
|
210
|
+
"id": 102,
|
|
190
211
|
"name": "spec/models/post_spec.rb"
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
"id": 103,
|
|
215
|
+
"name": "spec/models/comment_spec.rb"
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
"id": 104,
|
|
219
|
+
"name": "spec/models/profile_spec.rb"
|
|
191
220
|
}
|
|
192
221
|
],
|
|
193
|
-
"queue_state": "
|
|
222
|
+
"queue_state": "draining",
|
|
194
223
|
"remaining": {
|
|
195
|
-
"queued":
|
|
196
|
-
"assigned":
|
|
197
|
-
"completed":
|
|
224
|
+
"queued": 0,
|
|
225
|
+
"assigned": 4,
|
|
226
|
+
"completed": 0
|
|
198
227
|
}
|
|
199
228
|
},
|
|
200
229
|
"matchingRules": {
|
|
@@ -204,6 +233,12 @@
|
|
|
204
233
|
"$.body.tasks[1].id": {
|
|
205
234
|
"match": "type"
|
|
206
235
|
},
|
|
236
|
+
"$.body.tasks[2].id": {
|
|
237
|
+
"match": "type"
|
|
238
|
+
},
|
|
239
|
+
"$.body.tasks[3].id": {
|
|
240
|
+
"match": "type"
|
|
241
|
+
},
|
|
207
242
|
"$.body.remaining.queued": {
|
|
208
243
|
"match": "type"
|
|
209
244
|
},
|
|
@@ -221,15 +256,20 @@
|
|
|
221
256
|
"providerState": "queue is empty but not drained",
|
|
222
257
|
"request": {
|
|
223
258
|
"method": "post",
|
|
224
|
-
"path": "/
|
|
259
|
+
"path": "/api/v1/runs/abc123def456/claims",
|
|
225
260
|
"query": "batch=10",
|
|
226
261
|
"headers": {
|
|
227
262
|
"Authorization": "Bearer test-api-key",
|
|
228
263
|
"Content-Type": "application/json"
|
|
229
264
|
},
|
|
230
265
|
"body": {
|
|
231
|
-
"run_key": "test-run-key",
|
|
232
266
|
"worker_id": "worker-0"
|
|
267
|
+
},
|
|
268
|
+
"matchingRules": {
|
|
269
|
+
"$.path": {
|
|
270
|
+
"match": "regex",
|
|
271
|
+
"regex": "^\\/api\\/v1\\/runs\\/[0-9A-Za-z_-]{12}\\/claims$"
|
|
272
|
+
}
|
|
233
273
|
}
|
|
234
274
|
},
|
|
235
275
|
"response": {
|
|
@@ -244,14 +284,20 @@
|
|
|
244
284
|
"providerState": "queue is drained",
|
|
245
285
|
"request": {
|
|
246
286
|
"method": "post",
|
|
247
|
-
"path": "/
|
|
287
|
+
"path": "/api/v1/runs/abc123def456/claims",
|
|
248
288
|
"query": "batch=10",
|
|
249
289
|
"headers": {
|
|
250
|
-
"Authorization": "Bearer test-api-key"
|
|
290
|
+
"Authorization": "Bearer test-api-key",
|
|
291
|
+
"Content-Type": "application/json"
|
|
251
292
|
},
|
|
252
293
|
"body": {
|
|
253
|
-
"run_key": "test-run-key",
|
|
254
294
|
"worker_id": "worker-0"
|
|
295
|
+
},
|
|
296
|
+
"matchingRules": {
|
|
297
|
+
"$.path": {
|
|
298
|
+
"match": "regex",
|
|
299
|
+
"regex": "^\\/api\\/v1\\/runs\\/[0-9A-Za-z_-]{12}\\/claims$"
|
|
300
|
+
}
|
|
255
301
|
}
|
|
256
302
|
},
|
|
257
303
|
"response": {
|
|
@@ -264,12 +310,62 @@
|
|
|
264
310
|
}
|
|
265
311
|
}
|
|
266
312
|
},
|
|
313
|
+
{
|
|
314
|
+
"description": "a request to claim from cancelled run",
|
|
315
|
+
"providerState": "run is cancelled",
|
|
316
|
+
"request": {
|
|
317
|
+
"method": "post",
|
|
318
|
+
"path": "/api/v1/runs/abc123def456/claims",
|
|
319
|
+
"query": "batch=10",
|
|
320
|
+
"headers": {
|
|
321
|
+
"Authorization": "Bearer test-api-key",
|
|
322
|
+
"Content-Type": "application/json"
|
|
323
|
+
},
|
|
324
|
+
"body": {
|
|
325
|
+
"worker_id": "worker-0"
|
|
326
|
+
},
|
|
327
|
+
"matchingRules": {
|
|
328
|
+
"$.path": {
|
|
329
|
+
"match": "regex",
|
|
330
|
+
"regex": "^\\/api\\/v1\\/runs\\/[0-9A-Za-z_-]{12}\\/claims$"
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
},
|
|
334
|
+
"response": {
|
|
335
|
+
"status": 200,
|
|
336
|
+
"headers": {
|
|
337
|
+
"Content-Type": "application/json"
|
|
338
|
+
},
|
|
339
|
+
"body": {
|
|
340
|
+
"tasks": [
|
|
341
|
+
],
|
|
342
|
+
"queue_state": "cancelled",
|
|
343
|
+
"remaining": {
|
|
344
|
+
"queued": 0,
|
|
345
|
+
"assigned": 0,
|
|
346
|
+
"completed": 5
|
|
347
|
+
},
|
|
348
|
+
"should_stop": true
|
|
349
|
+
},
|
|
350
|
+
"matchingRules": {
|
|
351
|
+
"$.body.remaining.queued": {
|
|
352
|
+
"match": "type"
|
|
353
|
+
},
|
|
354
|
+
"$.body.remaining.assigned": {
|
|
355
|
+
"match": "type"
|
|
356
|
+
},
|
|
357
|
+
"$.body.remaining.completed": {
|
|
358
|
+
"match": "type"
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
},
|
|
267
363
|
{
|
|
268
364
|
"description": "a request to ack a task",
|
|
269
365
|
"providerState": "a task exists",
|
|
270
366
|
"request": {
|
|
271
367
|
"method": "post",
|
|
272
|
-
"path": "/tasks/
|
|
368
|
+
"path": "/api/v1/tasks/101/acknowledgment",
|
|
273
369
|
"headers": {
|
|
274
370
|
"Authorization": "Bearer test-api-key",
|
|
275
371
|
"Content-Type": "application/json"
|
|
@@ -282,6 +378,12 @@
|
|
|
282
378
|
"failures": [
|
|
283
379
|
]
|
|
284
380
|
}
|
|
381
|
+
},
|
|
382
|
+
"matchingRules": {
|
|
383
|
+
"$.path": {
|
|
384
|
+
"match": "regex",
|
|
385
|
+
"regex": "^\\/api\\/v1\\/tasks\\/\\d+\\/acknowledgment$"
|
|
386
|
+
}
|
|
285
387
|
}
|
|
286
388
|
},
|
|
287
389
|
"response": {
|
|
@@ -299,14 +401,19 @@
|
|
|
299
401
|
"providerState": "a worker exists",
|
|
300
402
|
"request": {
|
|
301
403
|
"method": "post",
|
|
302
|
-
"path": "/
|
|
404
|
+
"path": "/api/v1/runs/abc123def456/heartbeats",
|
|
303
405
|
"headers": {
|
|
304
406
|
"Authorization": "Bearer test-api-key",
|
|
305
407
|
"Content-Type": "application/json"
|
|
306
408
|
},
|
|
307
409
|
"body": {
|
|
308
|
-
"run_key": "test-run-key",
|
|
309
410
|
"worker_id": "worker-0"
|
|
411
|
+
},
|
|
412
|
+
"matchingRules": {
|
|
413
|
+
"$.path": {
|
|
414
|
+
"match": "regex",
|
|
415
|
+
"regex": "^\\/api\\/v1\\/runs\\/[0-9A-Za-z_-]{12}\\/heartbeats$"
|
|
416
|
+
}
|
|
310
417
|
}
|
|
311
418
|
},
|
|
312
419
|
"response": {
|
|
@@ -321,7 +428,7 @@
|
|
|
321
428
|
"providerState": "resource not found",
|
|
322
429
|
"request": {
|
|
323
430
|
"method": "get",
|
|
324
|
-
"path": "/runs/invalid-run
|
|
431
|
+
"path": "/api/v1/runs/invalid-run",
|
|
325
432
|
"headers": {
|
|
326
433
|
"Authorization": "Bearer test-api-key"
|
|
327
434
|
}
|
|
@@ -337,7 +444,7 @@
|
|
|
337
444
|
"providerState": "server error",
|
|
338
445
|
"request": {
|
|
339
446
|
"method": "post",
|
|
340
|
-
"path": "/runs
|
|
447
|
+
"path": "/api/v1/runs",
|
|
341
448
|
"headers": {
|
|
342
449
|
"Authorization": "Bearer test-api-key",
|
|
343
450
|
"Content-Type": "application/json"
|
|
@@ -359,7 +466,7 @@
|
|
|
359
466
|
"providerState": "unexpected redirect",
|
|
360
467
|
"request": {
|
|
361
468
|
"method": "post",
|
|
362
|
-
"path": "/runs
|
|
469
|
+
"path": "/api/v1/runs",
|
|
363
470
|
"headers": {
|
|
364
471
|
"Authorization": "Bearer test-api-key",
|
|
365
472
|
"Content-Type": "application/json"
|