vers-sdk 0.1.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 +7 -0
- data/lib/vers_sdk/client.rb +593 -0
- data/lib/vers_sdk/errors.rb +103 -0
- data/lib/vers_sdk/models.rb +1662 -0
- data/lib/vers_sdk.rb +12 -0
- metadata +49 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: fcd95ab2bbc99d7f862a7e7f7077df93bac046a3608c0bfafe49bbe67a418565
|
|
4
|
+
data.tar.gz: 4fda1d96575e7df3a08dd58d5bca38800778d3fe8400702903d5e0e07d7221e9
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: db9b02fc029aa89031586a14970458f656c2de5f7d02aabd4832417155765e7bfb521a387728b8b6a433b6c17b6549b510d9a2dd683f6f9bd517a4072aa9e8a9
|
|
7
|
+
data.tar.gz: 3f36deb765eea09d2fabbe31891b3a07788a990c9129cdf201d538bd48af8cf9dbaef72abb31062c38c488df1dec09a98e103bc9adecb3385ecf936cbd2daff1
|
|
@@ -0,0 +1,593 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Generated by Sterling SDK Generator
|
|
4
|
+
# Orchestrator Control Plane API v0.1.0
|
|
5
|
+
|
|
6
|
+
require "net/http"
|
|
7
|
+
require "uri"
|
|
8
|
+
require "json"
|
|
9
|
+
require "securerandom"
|
|
10
|
+
require "logger"
|
|
11
|
+
require "time"
|
|
12
|
+
|
|
13
|
+
require_relative "errors"
|
|
14
|
+
require_relative "models"
|
|
15
|
+
|
|
16
|
+
module VersSdk
|
|
17
|
+
# Per-request options that override client defaults.
|
|
18
|
+
class RequestOptions
|
|
19
|
+
attr_accessor :headers, :timeout
|
|
20
|
+
|
|
21
|
+
def initialize(headers: nil, timeout: nil)
|
|
22
|
+
@headers = headers
|
|
23
|
+
@timeout = timeout
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
LOG_LEVEL_MAP = {
|
|
28
|
+
"debug" => Logger::DEBUG,
|
|
29
|
+
"info" => Logger::INFO,
|
|
30
|
+
"warn" => Logger::WARN,
|
|
31
|
+
"error" => Logger::ERROR,
|
|
32
|
+
"off" => Logger::UNKNOWN
|
|
33
|
+
}.freeze
|
|
34
|
+
|
|
35
|
+
RETRYABLE_STATUS_CODES = [408, 409, 429].freeze
|
|
36
|
+
|
|
37
|
+
# HTTP client for Orchestrator Control Plane API.
|
|
38
|
+
class VersSdkClient
|
|
39
|
+
attr_reader :base_url, :max_retries, :timeout
|
|
40
|
+
|
|
41
|
+
# @param base_url [String, nil] API base URL (default: VERS_BASE_URL env or https://api.vers.sh)
|
|
42
|
+
# @param api_key [String, nil] Bearer token (default: VERS_API_KEY env)
|
|
43
|
+
# @param max_retries [Integer] Maximum number of retries (default: 2)
|
|
44
|
+
# @param timeout [Numeric] Request timeout in seconds (default: 30)
|
|
45
|
+
def initialize(base_url: nil, api_key: nil, max_retries: 2, timeout: 30)
|
|
46
|
+
@base_url = base_url || ENV.fetch("VERS_BASE_URL", "https://api.vers.sh")
|
|
47
|
+
@api_key = api_key || ENV["VERS_API_KEY"]
|
|
48
|
+
@max_retries = max_retries
|
|
49
|
+
@timeout = timeout
|
|
50
|
+
@logger = self.class.build_logger
|
|
51
|
+
@default_headers = {
|
|
52
|
+
"Content-Type" => "application/json",
|
|
53
|
+
"Accept" => "application/json",
|
|
54
|
+
"User-Agent" => "vers-sdk/0.1.1 ruby/#{RUBY_VERSION} #{RUBY_PLATFORM}"
|
|
55
|
+
}
|
|
56
|
+
@default_headers["Authorization"] = "Bearer #{@api_key}" if @api_key
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def self.build_logger
|
|
60
|
+
logger = Logger.new($stdout)
|
|
61
|
+
env_level = ENV.fetch("VERS_LOG", "warn").downcase
|
|
62
|
+
logger.level = LOG_LEVEL_MAP.fetch(env_level, Logger::WARN)
|
|
63
|
+
logger.formatter = proc { |severity, datetime, _progname, msg|
|
|
64
|
+
"#{datetime} [vers_sdk #{severity}] #{msg}\n"
|
|
65
|
+
}
|
|
66
|
+
logger
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
#
|
|
70
|
+
def resize_vm_disk(vm_id, body: nil, params: nil, options: nil)
|
|
71
|
+
path = sprintf("/api/v1/vm/%s/disk", vm_id)
|
|
72
|
+
query = {}
|
|
73
|
+
if params
|
|
74
|
+
h = params.is_a?(Hash) ? params : params.to_h
|
|
75
|
+
h.each { |k, v| query[k.to_s] = v.to_s unless v.nil? }
|
|
76
|
+
end
|
|
77
|
+
request("PATCH", path, options: options, body: body, query: query)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
#
|
|
81
|
+
def exec_vm_stream_attach(vm_id, body: nil, options: nil)
|
|
82
|
+
path = sprintf("/api/v1/vm/%s/exec/stream/attach", vm_id)
|
|
83
|
+
request("POST", path, options: options, body: body)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
#
|
|
87
|
+
def create_new_root_vm(body: nil, params: nil, options: nil)
|
|
88
|
+
path = "/api/v1/vm/new_root"
|
|
89
|
+
query = {}
|
|
90
|
+
if params
|
|
91
|
+
h = params.is_a?(Hash) ? params : params.to_h
|
|
92
|
+
h.each { |k, v| query[k.to_s] = v.to_s unless v.nil? }
|
|
93
|
+
end
|
|
94
|
+
request("POST", path, options: options, body: body, query: query)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
#
|
|
98
|
+
def vm_logs(vm_id, params: nil, options: nil)
|
|
99
|
+
path = sprintf("/api/v1/vm/%s/logs", vm_id)
|
|
100
|
+
query = {}
|
|
101
|
+
if params
|
|
102
|
+
h = params.is_a?(Hash) ? params : params.to_h
|
|
103
|
+
h.each { |k, v| query[k.to_s] = v.to_s unless v.nil? }
|
|
104
|
+
end
|
|
105
|
+
request("GET", path, options: options, query: query)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
#
|
|
109
|
+
def branch_by_ref(repo_name, tag_name, body: nil, params: nil, options: nil)
|
|
110
|
+
path = sprintf("/api/v1/vm/branch/by_ref/%s/%s", repo_name, tag_name)
|
|
111
|
+
query = {}
|
|
112
|
+
if params
|
|
113
|
+
h = params.is_a?(Hash) ? params : params.to_h
|
|
114
|
+
h.each { |k, v| query[k.to_s] = v.to_s unless v.nil? }
|
|
115
|
+
end
|
|
116
|
+
request("POST", path, options: options, body: body, query: query)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
#
|
|
120
|
+
def list_public_commits(options: nil)
|
|
121
|
+
path = "/api/v1/commits/public"
|
|
122
|
+
request("GET", path, options: options)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
#
|
|
126
|
+
def list_public_repo_tags(org_name, repo_name, options: nil)
|
|
127
|
+
path = sprintf("/api/v1/public/repositories/%s/%s/tags", org_name, repo_name)
|
|
128
|
+
request("GET", path, options: options)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
#
|
|
132
|
+
def branch_by_tag(tag_name, body: nil, params: nil, options: nil)
|
|
133
|
+
path = sprintf("/api/v1/vm/branch/by_tag/%s", tag_name)
|
|
134
|
+
query = {}
|
|
135
|
+
if params
|
|
136
|
+
h = params.is_a?(Hash) ? params : params.to_h
|
|
137
|
+
h.each { |k, v| query[k.to_s] = v.to_s unless v.nil? }
|
|
138
|
+
end
|
|
139
|
+
request("POST", path, options: options, body: body, query: query)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
#
|
|
143
|
+
def branch_vm(vm_or_commit_id, body: nil, params: nil, options: nil)
|
|
144
|
+
path = sprintf("/api/v1/vm/%s/branch", vm_or_commit_id)
|
|
145
|
+
query = {}
|
|
146
|
+
if params
|
|
147
|
+
h = params.is_a?(Hash) ? params : params.to_h
|
|
148
|
+
h.each { |k, v| query[k.to_s] = v.to_s unless v.nil? }
|
|
149
|
+
end
|
|
150
|
+
request("POST", path, options: options, body: body, query: query)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
#
|
|
154
|
+
def get_repo_tag(repo_name, tag_name, options: nil)
|
|
155
|
+
path = sprintf("/api/v1/repositories/%s/tags/%s", repo_name, tag_name)
|
|
156
|
+
request("GET", path, options: options)
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
#
|
|
160
|
+
def delete_repo_tag(repo_name, tag_name, options: nil)
|
|
161
|
+
path = sprintf("/api/v1/repositories/%s/tags/%s", repo_name, tag_name)
|
|
162
|
+
request("DELETE", path, options: options)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
#
|
|
166
|
+
def update_repo_tag(repo_name, tag_name, body: nil, options: nil)
|
|
167
|
+
path = sprintf("/api/v1/repositories/%s/tags/%s", repo_name, tag_name)
|
|
168
|
+
request("PATCH", path, options: options, body: body)
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
#
|
|
172
|
+
def get_repository(repo_name, options: nil)
|
|
173
|
+
path = sprintf("/api/v1/repositories/%s", repo_name)
|
|
174
|
+
request("GET", path, options: options)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
#
|
|
178
|
+
def delete_repository(repo_name, options: nil)
|
|
179
|
+
path = sprintf("/api/v1/repositories/%s", repo_name)
|
|
180
|
+
request("DELETE", path, options: options)
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
#
|
|
184
|
+
def list_parent_commits(commit_id, options: nil)
|
|
185
|
+
path = sprintf("/api/v1/vm/commits/%s/parents", commit_id)
|
|
186
|
+
request("GET", path, options: options)
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
#
|
|
190
|
+
def update_vm_state(vm_id, body: nil, params: nil, options: nil)
|
|
191
|
+
path = sprintf("/api/v1/vm/%s/state", vm_id)
|
|
192
|
+
query = {}
|
|
193
|
+
if params
|
|
194
|
+
h = params.is_a?(Hash) ? params : params.to_h
|
|
195
|
+
h.each { |k, v| query[k.to_s] = v.to_s unless v.nil? }
|
|
196
|
+
end
|
|
197
|
+
request("PATCH", path, options: options, body: body, query: query)
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
#
|
|
201
|
+
def fork_repository(body: nil, options: nil)
|
|
202
|
+
path = "/api/v1/repositories/fork"
|
|
203
|
+
request("POST", path, options: options, body: body)
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
#
|
|
207
|
+
def list_repositories(options: nil)
|
|
208
|
+
path = "/api/v1/repositories"
|
|
209
|
+
request("GET", path, options: options)
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
#
|
|
213
|
+
def create_repository(body: nil, options: nil)
|
|
214
|
+
path = "/api/v1/repositories"
|
|
215
|
+
request("POST", path, options: options, body: body)
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
#
|
|
219
|
+
def restore_from_commit(body: nil, options: nil)
|
|
220
|
+
path = "/api/v1/vm/from_commit"
|
|
221
|
+
request("POST", path, options: options, body: body)
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
#
|
|
225
|
+
def get_domain(domain_id, options: nil)
|
|
226
|
+
path = sprintf("/api/v1/domains/%s", domain_id)
|
|
227
|
+
request("GET", path, options: options)
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
#
|
|
231
|
+
def delete_domain(domain_id, options: nil)
|
|
232
|
+
path = sprintf("/api/v1/domains/%s", domain_id)
|
|
233
|
+
request("DELETE", path, options: options)
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
#
|
|
237
|
+
def list_tags(options: nil)
|
|
238
|
+
path = "/api/v1/commit_tags"
|
|
239
|
+
request("GET", path, options: options)
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
#
|
|
243
|
+
def create_tag(body: nil, options: nil)
|
|
244
|
+
path = "/api/v1/commit_tags"
|
|
245
|
+
request("POST", path, options: options, body: body)
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
#
|
|
249
|
+
def list_public_repositories(options: nil)
|
|
250
|
+
path = "/api/v1/public/repositories"
|
|
251
|
+
request("GET", path, options: options)
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
#
|
|
255
|
+
def get_public_repo_tag(org_name, repo_name, tag_name, options: nil)
|
|
256
|
+
path = sprintf("/api/v1/public/repositories/%s/%s/tags/%s", org_name, repo_name, tag_name)
|
|
257
|
+
request("GET", path, options: options)
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
#
|
|
261
|
+
def get_vm_metadata(vm_id, options: nil)
|
|
262
|
+
path = sprintf("/api/v1/vm/%s/metadata", vm_id)
|
|
263
|
+
request("GET", path, options: options)
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
#
|
|
267
|
+
def list_env_vars(options: nil)
|
|
268
|
+
path = "/api/v1/env_vars"
|
|
269
|
+
request("GET", path, options: options)
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
#
|
|
273
|
+
def set_env_vars(body: nil, options: nil)
|
|
274
|
+
path = "/api/v1/env_vars"
|
|
275
|
+
request("PUT", path, options: options, body: body)
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
#
|
|
279
|
+
def delete_vm(vm_id, params: nil, options: nil)
|
|
280
|
+
path = sprintf("/api/v1/vm/%s", vm_id)
|
|
281
|
+
query = {}
|
|
282
|
+
if params
|
|
283
|
+
h = params.is_a?(Hash) ? params : params.to_h
|
|
284
|
+
h.each { |k, v| query[k.to_s] = v.to_s unless v.nil? }
|
|
285
|
+
end
|
|
286
|
+
request("DELETE", path, options: options, query: query)
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
#
|
|
290
|
+
def list_commits(options: nil)
|
|
291
|
+
path = "/api/v1/commits"
|
|
292
|
+
request("GET", path, options: options)
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
#
|
|
296
|
+
def get_tag(tag_name, options: nil)
|
|
297
|
+
path = sprintf("/api/v1/commit_tags/%s", tag_name)
|
|
298
|
+
request("GET", path, options: options)
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
#
|
|
302
|
+
def delete_tag(tag_name, options: nil)
|
|
303
|
+
path = sprintf("/api/v1/commit_tags/%s", tag_name)
|
|
304
|
+
request("DELETE", path, options: options)
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
#
|
|
308
|
+
def update_tag(tag_name, body: nil, options: nil)
|
|
309
|
+
path = sprintf("/api/v1/commit_tags/%s", tag_name)
|
|
310
|
+
request("PATCH", path, options: options, body: body)
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
#
|
|
314
|
+
def branch_by_vm(vm_id, body: nil, params: nil, options: nil)
|
|
315
|
+
path = sprintf("/api/v1/vm/branch/by_vm/%s", vm_id)
|
|
316
|
+
query = {}
|
|
317
|
+
if params
|
|
318
|
+
h = params.is_a?(Hash) ? params : params.to_h
|
|
319
|
+
h.each { |k, v| query[k.to_s] = v.to_s unless v.nil? }
|
|
320
|
+
end
|
|
321
|
+
request("POST", path, options: options, body: body, query: query)
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
#
|
|
325
|
+
def delete_env_var(key, options: nil)
|
|
326
|
+
path = sprintf("/api/v1/env_vars/%s", key)
|
|
327
|
+
request("DELETE", path, options: options)
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
#
|
|
331
|
+
def exec_vm_stream(vm_id, body: nil, options: nil)
|
|
332
|
+
path = sprintf("/api/v1/vm/%s/exec/stream", vm_id)
|
|
333
|
+
request("POST", path, options: options, body: body)
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
#
|
|
337
|
+
def commit_vm(vm_id, body: nil, params: nil, options: nil)
|
|
338
|
+
path = sprintf("/api/v1/vm/%s/commit", vm_id)
|
|
339
|
+
query = {}
|
|
340
|
+
if params
|
|
341
|
+
h = params.is_a?(Hash) ? params : params.to_h
|
|
342
|
+
h.each { |k, v| query[k.to_s] = v.to_s unless v.nil? }
|
|
343
|
+
end
|
|
344
|
+
request("POST", path, options: options, body: body, query: query)
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
#
|
|
348
|
+
def delete_commit(commit_id, options: nil)
|
|
349
|
+
path = sprintf("/api/v1/commits/%s", commit_id)
|
|
350
|
+
request("DELETE", path, options: options)
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
#
|
|
354
|
+
def update_commit(commit_id, body: nil, options: nil)
|
|
355
|
+
path = sprintf("/api/v1/commits/%s", commit_id)
|
|
356
|
+
request("PATCH", path, options: options, body: body)
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
#
|
|
360
|
+
def list_repo_tags(repo_name, options: nil)
|
|
361
|
+
path = sprintf("/api/v1/repositories/%s/tags", repo_name)
|
|
362
|
+
request("GET", path, options: options)
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
#
|
|
366
|
+
def create_repo_tag(repo_name, body: nil, options: nil)
|
|
367
|
+
path = sprintf("/api/v1/repositories/%s/tags", repo_name)
|
|
368
|
+
request("POST", path, options: options, body: body)
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
#
|
|
372
|
+
def ssh_key(vm_id, options: nil)
|
|
373
|
+
path = sprintf("/api/v1/vm/%s/ssh_key", vm_id)
|
|
374
|
+
request("GET", path, options: options)
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
#
|
|
378
|
+
def exec_vm(vm_id, body: nil, options: nil)
|
|
379
|
+
path = sprintf("/api/v1/vm/%s/exec", vm_id)
|
|
380
|
+
request("POST", path, options: options, body: body)
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
#
|
|
384
|
+
def set_repository_visibility(repo_name, body: nil, options: nil)
|
|
385
|
+
path = sprintf("/api/v1/repositories/%s/visibility", repo_name)
|
|
386
|
+
request("PATCH", path, options: options, body: body)
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
#
|
|
390
|
+
def list_vms(options: nil)
|
|
391
|
+
path = "/api/v1/vms"
|
|
392
|
+
request("GET", path, options: options)
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
#
|
|
396
|
+
def list_domains(params: nil, options: nil)
|
|
397
|
+
path = "/api/v1/domains"
|
|
398
|
+
query = {}
|
|
399
|
+
if params
|
|
400
|
+
h = params.is_a?(Hash) ? params : params.to_h
|
|
401
|
+
h.each { |k, v| query[k.to_s] = v.to_s unless v.nil? }
|
|
402
|
+
end
|
|
403
|
+
request("GET", path, options: options, query: query)
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
#
|
|
407
|
+
def create_domain(body: nil, options: nil)
|
|
408
|
+
path = "/api/v1/domains"
|
|
409
|
+
request("POST", path, options: options, body: body)
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
#
|
|
413
|
+
def vm_status(vm_id, options: nil)
|
|
414
|
+
path = sprintf("/api/v1/vm/%s/status", vm_id)
|
|
415
|
+
request("GET", path, options: options)
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
#
|
|
419
|
+
def get_public_repository(org_name, repo_name, options: nil)
|
|
420
|
+
path = sprintf("/api/v1/public/repositories/%s/%s", org_name, repo_name)
|
|
421
|
+
request("GET", path, options: options)
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
#
|
|
425
|
+
def branch_by_commit(commit_id, body: nil, params: nil, options: nil)
|
|
426
|
+
path = sprintf("/api/v1/vm/branch/by_commit/%s", commit_id)
|
|
427
|
+
query = {}
|
|
428
|
+
if params
|
|
429
|
+
h = params.is_a?(Hash) ? params : params.to_h
|
|
430
|
+
h.each { |k, v| query[k.to_s] = v.to_s unless v.nil? }
|
|
431
|
+
end
|
|
432
|
+
request("POST", path, options: options, body: body, query: query)
|
|
433
|
+
end
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
private
|
|
437
|
+
|
|
438
|
+
def request(method, path, options: nil, body: nil, query: nil)
|
|
439
|
+
uri = build_uri(path, query)
|
|
440
|
+
@logger.debug("request: #{method} #{uri}")
|
|
441
|
+
|
|
442
|
+
last_exc = nil
|
|
443
|
+
retry_after_used = false
|
|
444
|
+
|
|
445
|
+
(@max_retries + 1).times do |attempt|
|
|
446
|
+
if attempt > 0 && !retry_after_used
|
|
447
|
+
delay = retry_delay(attempt - 1)
|
|
448
|
+
@logger.info("retry attempt #{attempt}/#{@max_retries} for #{method} #{path}")
|
|
449
|
+
sleep(delay)
|
|
450
|
+
end
|
|
451
|
+
retry_after_used = false
|
|
452
|
+
|
|
453
|
+
begin
|
|
454
|
+
response = execute_request(method, uri, body: body, options: options)
|
|
455
|
+
status = response.code.to_i
|
|
456
|
+
@logger.debug("response: #{status} for #{method} #{path}")
|
|
457
|
+
|
|
458
|
+
if retryable_status?(status) && attempt < @max_retries
|
|
459
|
+
ra_delay = parse_retry_after(response["Retry-After"])
|
|
460
|
+
if ra_delay
|
|
461
|
+
retry_after_used = true
|
|
462
|
+
@logger.info("retry attempt #{attempt + 1}/#{@max_retries} for #{method} #{path} (retry-after: #{ra_delay}s)")
|
|
463
|
+
sleep(ra_delay)
|
|
464
|
+
end
|
|
465
|
+
last_exc = StandardError.new("HTTP #{status}")
|
|
466
|
+
next
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
check_response(response)
|
|
470
|
+
return response
|
|
471
|
+
rescue APIError
|
|
472
|
+
raise
|
|
473
|
+
rescue Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::ETIMEDOUT,
|
|
474
|
+
Net::OpenTimeout, Net::ReadTimeout, SocketError => e
|
|
475
|
+
@logger.error("connection error: #{method} #{path} - #{e.message}")
|
|
476
|
+
raise APIConnectionError.new(message: e.message, cause: e) if attempt == @max_retries
|
|
477
|
+
|
|
478
|
+
last_exc = e
|
|
479
|
+
end
|
|
480
|
+
end
|
|
481
|
+
|
|
482
|
+
raise last_exc if last_exc
|
|
483
|
+
end
|
|
484
|
+
|
|
485
|
+
def build_uri(path, query)
|
|
486
|
+
uri = URI.join(@base_url, path)
|
|
487
|
+
if query && !query.empty?
|
|
488
|
+
uri.query = URI.encode_www_form(query)
|
|
489
|
+
end
|
|
490
|
+
uri
|
|
491
|
+
end
|
|
492
|
+
|
|
493
|
+
def execute_request(method, uri, body: nil, options: nil)
|
|
494
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
495
|
+
http.use_ssl = (uri.scheme == "https")
|
|
496
|
+
effective_timeout = (options&.timeout || @timeout)
|
|
497
|
+
http.open_timeout = effective_timeout
|
|
498
|
+
http.read_timeout = effective_timeout
|
|
499
|
+
|
|
500
|
+
req = build_http_request(method, uri, body)
|
|
501
|
+
|
|
502
|
+
# Merge default headers
|
|
503
|
+
@default_headers.each { |k, v| req[k] = v }
|
|
504
|
+
|
|
505
|
+
# Idempotency key for mutating methods
|
|
506
|
+
if %w[POST PUT PATCH DELETE].include?(method)
|
|
507
|
+
req["X-Idempotency-Key"] = SecureRandom.uuid
|
|
508
|
+
end
|
|
509
|
+
|
|
510
|
+
# Per-request header overrides
|
|
511
|
+
if options&.headers
|
|
512
|
+
options.headers.each do |k, v|
|
|
513
|
+
if v.nil?
|
|
514
|
+
req.delete(k)
|
|
515
|
+
else
|
|
516
|
+
req[k] = v
|
|
517
|
+
end
|
|
518
|
+
end
|
|
519
|
+
end
|
|
520
|
+
|
|
521
|
+
http.request(req)
|
|
522
|
+
end
|
|
523
|
+
|
|
524
|
+
def build_http_request(method, uri, body)
|
|
525
|
+
request_path = uri.request_uri
|
|
526
|
+
case method
|
|
527
|
+
when "GET"
|
|
528
|
+
Net::HTTP::Get.new(request_path)
|
|
529
|
+
when "POST"
|
|
530
|
+
req = Net::HTTP::Post.new(request_path)
|
|
531
|
+
req.body = body.to_json if body
|
|
532
|
+
req
|
|
533
|
+
when "PUT"
|
|
534
|
+
req = Net::HTTP::Put.new(request_path)
|
|
535
|
+
req.body = body.to_json if body
|
|
536
|
+
req
|
|
537
|
+
when "PATCH"
|
|
538
|
+
req = Net::HTTP::Patch.new(request_path)
|
|
539
|
+
req.body = body.to_json if body
|
|
540
|
+
req
|
|
541
|
+
when "DELETE"
|
|
542
|
+
Net::HTTP::Delete.new(request_path)
|
|
543
|
+
else
|
|
544
|
+
Net::HTTP::Get.new(request_path)
|
|
545
|
+
end
|
|
546
|
+
end
|
|
547
|
+
|
|
548
|
+
def check_response(response)
|
|
549
|
+
status = response.code.to_i
|
|
550
|
+
return if status >= 200 && status < 300
|
|
551
|
+
|
|
552
|
+
body = nil
|
|
553
|
+
message = nil
|
|
554
|
+
begin
|
|
555
|
+
body = JSON.parse(response.body)
|
|
556
|
+
rescue StandardError
|
|
557
|
+
message = response.body
|
|
558
|
+
end
|
|
559
|
+
|
|
560
|
+
headers = {}
|
|
561
|
+
response.each_header { |k, v| headers[k] = v }
|
|
562
|
+
|
|
563
|
+
raise APIError.generate(status: status, body: body, message: message, headers: headers)
|
|
564
|
+
end
|
|
565
|
+
|
|
566
|
+
def retryable_status?(status)
|
|
567
|
+
status >= 500 || RETRYABLE_STATUS_CODES.include?(status)
|
|
568
|
+
end
|
|
569
|
+
|
|
570
|
+
def retry_delay(attempt)
|
|
571
|
+
base = 0.5 * (2**attempt)
|
|
572
|
+
jitter = rand * base * 0.25
|
|
573
|
+
base + jitter
|
|
574
|
+
end
|
|
575
|
+
|
|
576
|
+
def parse_retry_after(header_value)
|
|
577
|
+
return nil if header_value.nil?
|
|
578
|
+
|
|
579
|
+
# Try numeric
|
|
580
|
+
delay = Float(header_value)
|
|
581
|
+
return [[delay, 0.0].max, 60.0].min
|
|
582
|
+
rescue ArgumentError, TypeError
|
|
583
|
+
# Try HTTP date
|
|
584
|
+
begin
|
|
585
|
+
retry_time = Time.httpdate(header_value)
|
|
586
|
+
delay = retry_time - Time.now
|
|
587
|
+
[[delay, 0.0].max, 60.0].min
|
|
588
|
+
rescue ArgumentError
|
|
589
|
+
nil
|
|
590
|
+
end
|
|
591
|
+
end
|
|
592
|
+
end
|
|
593
|
+
end
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Generated by Sterling SDK Generator
|
|
4
|
+
# Orchestrator Control Plane API v0.1.0
|
|
5
|
+
|
|
6
|
+
module VersSdk
|
|
7
|
+
# Base error class for the VersSdk SDK.
|
|
8
|
+
class VersSDKError < StandardError; end
|
|
9
|
+
|
|
10
|
+
# Error returned when the API responds with a non-success status code.
|
|
11
|
+
class APIError < VersSDKError
|
|
12
|
+
attr_reader :status, :headers, :body
|
|
13
|
+
|
|
14
|
+
def initialize(status: nil, body: nil, message: nil, headers: nil)
|
|
15
|
+
@status = status
|
|
16
|
+
@headers = headers
|
|
17
|
+
@body = body
|
|
18
|
+
super(make_message(status, body, message))
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.generate(status:, body: nil, message: nil, headers: nil)
|
|
22
|
+
return APIConnectionError.new(message: message) if status.nil? || headers.nil?
|
|
23
|
+
|
|
24
|
+
error_map = {
|
|
25
|
+
400 => BadRequestError,
|
|
26
|
+
401 => AuthenticationError,
|
|
27
|
+
403 => PermissionDeniedError,
|
|
28
|
+
404 => NotFoundError,
|
|
29
|
+
409 => ConflictError,
|
|
30
|
+
422 => UnprocessableEntityError,
|
|
31
|
+
429 => RateLimitError
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
error_cls = error_map[status]
|
|
35
|
+
if error_cls
|
|
36
|
+
return error_cls.new(status: status, body: body, message: message, headers: headers)
|
|
37
|
+
end
|
|
38
|
+
if status >= 500
|
|
39
|
+
return InternalServerError.new(status: status, body: body, message: message, headers: headers)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
new(status: status, body: body, message: message, headers: headers)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def make_message(status, body, message)
|
|
48
|
+
msg = nil
|
|
49
|
+
if body.is_a?(Hash) && body.key?("message")
|
|
50
|
+
msg = body["message"].to_s
|
|
51
|
+
elsif !body.nil?
|
|
52
|
+
msg = body.to_s
|
|
53
|
+
elsif !message.nil?
|
|
54
|
+
msg = message
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
return "#{status} #{msg}" if status && msg
|
|
58
|
+
return "#{status} status code (no body)" if status
|
|
59
|
+
return msg if msg
|
|
60
|
+
|
|
61
|
+
"(no status code or body)"
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Error thrown when a connection to the API cannot be established.
|
|
66
|
+
class APIConnectionError < APIError
|
|
67
|
+
def initialize(message: nil, cause: nil)
|
|
68
|
+
@cause = cause
|
|
69
|
+
super(status: nil, body: nil, message: message || "Connection error.")
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Error thrown when a request times out.
|
|
74
|
+
class APIConnectionTimeoutError < APIConnectionError
|
|
75
|
+
def initialize(message: nil)
|
|
76
|
+
super(message: message || "Request timed out.")
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# 400 Bad Request
|
|
81
|
+
class BadRequestError < APIError; end
|
|
82
|
+
|
|
83
|
+
# 401 Unauthorized
|
|
84
|
+
class AuthenticationError < APIError; end
|
|
85
|
+
|
|
86
|
+
# 403 Forbidden
|
|
87
|
+
class PermissionDeniedError < APIError; end
|
|
88
|
+
|
|
89
|
+
# 404 Not Found
|
|
90
|
+
class NotFoundError < APIError; end
|
|
91
|
+
|
|
92
|
+
# 409 Conflict
|
|
93
|
+
class ConflictError < APIError; end
|
|
94
|
+
|
|
95
|
+
# 422 Unprocessable Entity
|
|
96
|
+
class UnprocessableEntityError < APIError; end
|
|
97
|
+
|
|
98
|
+
# 429 Too Many Requests
|
|
99
|
+
class RateLimitError < APIError; end
|
|
100
|
+
|
|
101
|
+
# 5xx Internal Server Error
|
|
102
|
+
class InternalServerError < APIError; end
|
|
103
|
+
end
|