gretl 1.0.3 → 1.0.5
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/lib/gretl.rb +196 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 96aab4c6f7f37f115ae1b8db4f531374f4aa37e4cd7da305b87f24c97a8e6bb0
|
|
4
|
+
data.tar.gz: 5607a48037a46aeb238cc01c982a6c9819fae444b03bd0d66ee33a8e77e12230
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5f338845601daefa91c8bd5a2a31facaed2afbc7b0b648896d1913dab2c90c175b55d3d09da0d0a1705e28f535c54d3ac24e7fdced128196e3f994ca216ee0f7
|
|
7
|
+
data.tar.gz: 632f13e0748e22208bb452479d6fe7a877eb16f374570589286bc7b3e1aabb96b684962319be967c3579ca1c2a579a0e16ac44f2db8839d95850071d7b573dd4
|
data/lib/gretl.rb
CHANGED
|
@@ -129,6 +129,201 @@ module Gretl
|
|
|
129
129
|
raise "Port matching #{query.inspect} did not become active within #{timeout}s"
|
|
130
130
|
end
|
|
131
131
|
|
|
132
|
+
def add(port, name: nil, cmd: nil, group: nil, cwd: nil)
|
|
133
|
+
Gretl._ensure_daemon
|
|
134
|
+
uri = URI("#{Gretl::BASE}/ports")
|
|
135
|
+
body = { port: port }
|
|
136
|
+
body[:name] = name if name
|
|
137
|
+
body[:cmd] = cmd if cmd
|
|
138
|
+
body[:group] = group if group
|
|
139
|
+
body[:cwd] = cwd if cwd
|
|
140
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
141
|
+
req = Net::HTTP::Post.new(uri, "Content-Type" => "application/json")
|
|
142
|
+
req.body = body.to_json
|
|
143
|
+
res = http.request(req)
|
|
144
|
+
data = JSON.parse(res.body, symbolize_names: true)
|
|
145
|
+
Gretl::PortInfo.new(**data.slice(*Gretl::PortInfo.members))
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def wait_for_inactive(query, timeout: 30, interval: 0.5)
|
|
149
|
+
deadline = Time.now + timeout
|
|
150
|
+
while Time.now < deadline
|
|
151
|
+
Gretl._resolve(query).each { |p| return p if p.stopped? }
|
|
152
|
+
sleep interval
|
|
153
|
+
end
|
|
154
|
+
raise "Port matching #{query.inspect} did not become inactive within #{timeout}s"
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
CLOUD_API = "https://api.gretl.dev"
|
|
159
|
+
|
|
160
|
+
class K8sClient
|
|
161
|
+
def initialize(token)
|
|
162
|
+
@token = token
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def clusters
|
|
166
|
+
_cloud_get("/k8s/clusters")
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def workloads(cluster_id: nil)
|
|
170
|
+
path = cluster_id ? "/k8s/clusters/#{cluster_id}/workloads" : "/k8s/workloads"
|
|
171
|
+
_cloud_get(path)
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def sync(cluster_id)
|
|
175
|
+
_cloud_post("/k8s/clusters/#{cluster_id}/sync")
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def sleep(workload_id)
|
|
179
|
+
_cloud_post("/k8s/workloads/#{workload_id}/sleep")
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def wake(workload_id)
|
|
183
|
+
_cloud_post("/k8s/workloads/#{workload_id}/wake")
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def tether(workload_id, container_port:, label: nil)
|
|
187
|
+
body = { container_port: container_port }
|
|
188
|
+
body[:label] = label if label
|
|
189
|
+
_cloud_post("/k8s/workloads/#{workload_id}/tether", body)
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def detach(tether_id)
|
|
193
|
+
_cloud_post("/k8s/tethers/#{tether_id}/deactivate")
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def tethers
|
|
197
|
+
_cloud_get("/k8s/tethers")
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def auto_sleep(workload_id)
|
|
201
|
+
_cloud_get("/k8s/workloads/#{workload_id}/auto-sleep")
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def set_auto_sleep(workload_id, enabled:, idle_timeout_minutes:)
|
|
205
|
+
_cloud_put("/k8s/workloads/#{workload_id}/auto-sleep",
|
|
206
|
+
{ auto_sleep_enabled: enabled, idle_timeout_minutes: idle_timeout_minutes })
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
private
|
|
210
|
+
|
|
211
|
+
def _cloud_get(path)
|
|
212
|
+
uri = URI("#{CLOUD_API}#{path}")
|
|
213
|
+
req = Net::HTTP::Get.new(uri, "Authorization" => "Bearer #{@token}")
|
|
214
|
+
_cloud_call(uri, req)
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def _cloud_post(path, body = nil)
|
|
218
|
+
uri = URI("#{CLOUD_API}#{path}")
|
|
219
|
+
req = Net::HTTP::Post.new(uri, "Authorization" => "Bearer #{@token}", "Content-Type" => "application/json")
|
|
220
|
+
req.body = body.to_json if body
|
|
221
|
+
_cloud_call(uri, req)
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def _cloud_put(path, body)
|
|
225
|
+
uri = URI("#{CLOUD_API}#{path}")
|
|
226
|
+
req = Net::HTTP::Put.new(uri, "Authorization" => "Bearer #{@token}", "Content-Type" => "application/json")
|
|
227
|
+
req.body = body.to_json
|
|
228
|
+
_cloud_call(uri, req)
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def _cloud_call(uri, req)
|
|
232
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
233
|
+
http.use_ssl = uri.scheme == "https"
|
|
234
|
+
http.open_timeout = 10
|
|
235
|
+
http.read_timeout = 10
|
|
236
|
+
res = http.request(req)
|
|
237
|
+
raise "Gretl API error #{res.code}: #{res.body}" unless res.is_a?(Net::HTTPSuccess)
|
|
238
|
+
JSON.parse(res.body, symbolize_names: true)
|
|
239
|
+
rescue Errno::ECONNREFUSED => e
|
|
240
|
+
raise "Cannot reach Gretl API: #{e}"
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
class Client
|
|
245
|
+
def initialize(token: nil)
|
|
246
|
+
_token = token || ENV.fetch("GR_TOKEN", "")
|
|
247
|
+
@k8s = K8sClient.new(_token)
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
attr_reader :k8s
|
|
251
|
+
|
|
252
|
+
def ports = Gretl._fetch_ports
|
|
253
|
+
def port(query) = Gretl._resolve(query)
|
|
254
|
+
def active = Gretl._fetch_ports.select(&:active)
|
|
255
|
+
def inactive = Gretl._fetch_ports.reject(&:active)
|
|
256
|
+
|
|
257
|
+
def start(query)
|
|
258
|
+
matches = Gretl._resolve(query)
|
|
259
|
+
raise ArgumentError, "No port found matching #{query.inspect}" if matches.empty?
|
|
260
|
+
matches.each do |p|
|
|
261
|
+
Gretl._request(:post, "/ports/#{p.port}/start") if !p.active && p.has_command
|
|
262
|
+
end
|
|
263
|
+
matches
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def stop(query)
|
|
267
|
+
matches = Gretl._resolve(query)
|
|
268
|
+
raise ArgumentError, "No port found matching #{query.inspect}" if matches.empty?
|
|
269
|
+
matches.each do |p|
|
|
270
|
+
Gretl._request(:post, "/ports/#{p.port}/stop") if p.active
|
|
271
|
+
end
|
|
272
|
+
matches
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
def start_group(group)
|
|
276
|
+
matches = Gretl._fetch_ports.select { |p| p.group&.downcase == group.downcase }
|
|
277
|
+
raise ArgumentError, "No ports found in group #{group.inspect}" if matches.empty?
|
|
278
|
+
matches.each { |p| Gretl._request(:post, "/ports/#{p.port}/start") if !p.active && p.has_command }
|
|
279
|
+
matches
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
def stop_group(group)
|
|
283
|
+
matches = Gretl._fetch_ports.select { |p| p.group&.downcase == group.downcase }
|
|
284
|
+
raise ArgumentError, "No ports found in group #{group.inspect}" if matches.empty?
|
|
285
|
+
matches.each { |p| Gretl._request(:post, "/ports/#{p.port}/stop") if p.active }
|
|
286
|
+
matches
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
def status_group(group)
|
|
290
|
+
matches = Gretl._fetch_ports.select { |p| p.group&.downcase == group.downcase }
|
|
291
|
+
raise ArgumentError, "No ports found in group #{group.inspect}" if matches.empty?
|
|
292
|
+
matches
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
def remove(query)
|
|
296
|
+
matches = Gretl._resolve(query)
|
|
297
|
+
raise ArgumentError, "No port found matching #{query.inspect}" if matches.empty?
|
|
298
|
+
matches.each { |p| Gretl._request(:delete, "/ports/#{p.port}") }
|
|
299
|
+
matches
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
def wait_for_active(query, timeout: 30, interval: 0.5)
|
|
303
|
+
deadline = Time.now + timeout
|
|
304
|
+
while Time.now < deadline
|
|
305
|
+
Gretl._resolve(query).each { |p| return p if p.active }
|
|
306
|
+
sleep interval
|
|
307
|
+
end
|
|
308
|
+
raise "Port matching #{query.inspect} did not become active within #{timeout}s"
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
def add(port, name: nil, cmd: nil, group: nil, cwd: nil)
|
|
312
|
+
Gretl._ensure_daemon
|
|
313
|
+
uri = URI("#{Gretl::BASE}/ports")
|
|
314
|
+
body = { port: port }
|
|
315
|
+
body[:name] = name if name
|
|
316
|
+
body[:cmd] = cmd if cmd
|
|
317
|
+
body[:group] = group if group
|
|
318
|
+
body[:cwd] = cwd if cwd
|
|
319
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
320
|
+
req = Net::HTTP::Post.new(uri, "Content-Type" => "application/json")
|
|
321
|
+
req.body = body.to_json
|
|
322
|
+
res = http.request(req)
|
|
323
|
+
data = JSON.parse(res.body, symbolize_names: true)
|
|
324
|
+
Gretl::PortInfo.new(**data.slice(*Gretl::PortInfo.members))
|
|
325
|
+
end
|
|
326
|
+
|
|
132
327
|
def wait_for_inactive(query, timeout: 30, interval: 0.5)
|
|
133
328
|
deadline = Time.now + timeout
|
|
134
329
|
while Time.now < deadline
|
|
@@ -139,6 +334,6 @@ module Gretl
|
|
|
139
334
|
end
|
|
140
335
|
end
|
|
141
336
|
|
|
142
|
-
# Default client
|
|
337
|
+
# Default client — token read from GR_TOKEN env var
|
|
143
338
|
PA = Client.new
|
|
144
339
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: gretl
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Gretl
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-05-
|
|
11
|
+
date: 2026-05-18 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description:
|
|
14
14
|
email:
|