radfish 0.3.0 → 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/README.md +14 -0
- data/lib/radfish/core/session.rb +19 -17
- data/lib/radfish/http_client.rb +31 -7
- data/lib/radfish/version.rb +1 -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: 28b19abfcfc47b88d31ad43a17e67b9f0de71ac358c44bdb8e7e6f91b80a775f
|
|
4
|
+
data.tar.gz: d6e34c43743f79bfbfbb874545deedf1a487bfdfd6c7111683baebad257b5f45
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0c62952bdd04c892f39f6fe44785bfd8f7b12800f27eaac6ef7ba2555380027401acfb2ed76fe5a0d4628917ababcde8fd4dba12dd61bc0ecfa88ea97c9d9b0c
|
|
7
|
+
data.tar.gz: 51a2cd9988e9b3e39e4af3c382308f48128aa52d3f5ab52857e4b11425e775f996a559fc1d77a1353d321e832c8e5266336bf597f55b897666be4e6c8981a962
|
data/README.md
CHANGED
|
@@ -126,6 +126,20 @@ Regardless of vendor, all adapters provide:
|
|
|
126
126
|
### Vendor-Specific Features
|
|
127
127
|
Adapters can expose vendor-specific functionality while maintaining the common interface.
|
|
128
128
|
|
|
129
|
+
## Compatibility
|
|
130
|
+
|
|
131
|
+
`activesupport` below 8.1 and `json` 3 cannot be used together: activesupport's
|
|
132
|
+
JSON encoder calls `JSON.generate(..., quirks_mode: true)`, and json 3 removed
|
|
133
|
+
that keyword, so any `hash.to_json` raises `ArgumentError: unknown keyword:
|
|
134
|
+
quirks_mode`. This is not specific to this gem — it bites anything that loads
|
|
135
|
+
`active_support/core_ext` — but radfish depends on activesupport, so pick one
|
|
136
|
+
of:
|
|
137
|
+
|
|
138
|
+
- activesupport >= 8.1 with json 3, or
|
|
139
|
+
- activesupport 7.x with json 2.
|
|
140
|
+
|
|
141
|
+
CI covers both ends.
|
|
142
|
+
|
|
129
143
|
## Installation
|
|
130
144
|
|
|
131
145
|
Add to your Gemfile:
|
data/lib/radfish/core/session.rb
CHANGED
|
@@ -14,9 +14,14 @@ module Radfish
|
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
def connection
|
|
17
|
-
@connection ||=
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
@connection ||= begin
|
|
18
|
+
url = URI(client.base_url)
|
|
19
|
+
HttpClient.new(host: url.host, port: url.port,
|
|
20
|
+
use_ssl: url.scheme == 'https', verify_ssl: client.verify_ssl,
|
|
21
|
+
host_header: client.host_header,
|
|
22
|
+
retry_count: client.retry_count,
|
|
23
|
+
retry_delay: client.retry_delay,
|
|
24
|
+
verbosity: verbosity)
|
|
20
25
|
end
|
|
21
26
|
end
|
|
22
27
|
|
|
@@ -32,10 +37,9 @@ module Radfish
|
|
|
32
37
|
'Content-Type' => 'application/json',
|
|
33
38
|
'Accept' => 'application/json'
|
|
34
39
|
}
|
|
35
|
-
|
|
36
|
-
|
|
40
|
+
|
|
37
41
|
begin
|
|
38
|
-
response = connection.post('/redfish/v1/SessionService/Sessions', payload, headers)
|
|
42
|
+
response = connection.post('/redfish/v1/SessionService/Sessions', body: payload, headers: headers)
|
|
39
43
|
|
|
40
44
|
if response.status == 201
|
|
41
45
|
@x_auth_token = response.headers['x-auth-token']
|
|
@@ -50,14 +54,14 @@ module Radfish
|
|
|
50
54
|
rescue JSON::ParserError
|
|
51
55
|
end
|
|
52
56
|
|
|
53
|
-
debug "Session created successfully
|
|
57
|
+
debug "Session #{@session_id || '?'} created successfully (token #{@x_auth_token ? 'received' : 'missing'})", 1, :green
|
|
54
58
|
return true
|
|
55
59
|
else
|
|
56
60
|
debug "Failed to create session. Status: #{response.status}", 1, :red
|
|
57
|
-
debug "Response: #{response.body}", 2
|
|
61
|
+
debug "Response: #{HttpClient.scrub(response.body)}", 2
|
|
58
62
|
return false
|
|
59
63
|
end
|
|
60
|
-
rescue
|
|
64
|
+
rescue Radfish::Error => e
|
|
61
65
|
debug "Connection error creating session: #{e.message}", 1, :red
|
|
62
66
|
return false
|
|
63
67
|
end
|
|
@@ -72,10 +76,9 @@ module Radfish
|
|
|
72
76
|
'X-Auth-Token' => @x_auth_token,
|
|
73
77
|
'Accept' => 'application/json'
|
|
74
78
|
}
|
|
75
|
-
|
|
76
|
-
|
|
79
|
+
|
|
77
80
|
begin
|
|
78
|
-
response = connection.delete("/redfish/v1/SessionService/Sessions/#{@session_id}",
|
|
81
|
+
response = connection.delete("/redfish/v1/SessionService/Sessions/#{@session_id}", headers: headers)
|
|
79
82
|
|
|
80
83
|
if response.status == 204 || response.status == 200
|
|
81
84
|
debug "Session deleted successfully", 1, :green
|
|
@@ -86,7 +89,7 @@ module Radfish
|
|
|
86
89
|
debug "Failed to delete session. Status: #{response.status}", 1, :yellow
|
|
87
90
|
return false
|
|
88
91
|
end
|
|
89
|
-
rescue
|
|
92
|
+
rescue Radfish::Error => e
|
|
90
93
|
debug "Error deleting session: #{e.message}", 1, :yellow
|
|
91
94
|
return false
|
|
92
95
|
end
|
|
@@ -99,10 +102,9 @@ module Radfish
|
|
|
99
102
|
'X-Auth-Token' => @x_auth_token,
|
|
100
103
|
'Accept' => 'application/json'
|
|
101
104
|
}
|
|
102
|
-
|
|
103
|
-
|
|
105
|
+
|
|
104
106
|
begin
|
|
105
|
-
response = connection.get("/redfish/v1/SessionService/Sessions/#{@session_id}",
|
|
107
|
+
response = connection.get("/redfish/v1/SessionService/Sessions/#{@session_id}", headers: headers)
|
|
106
108
|
response.status == 200
|
|
107
109
|
rescue
|
|
108
110
|
false
|
|
@@ -114,4 +116,4 @@ module Radfish
|
|
|
114
116
|
end
|
|
115
117
|
end
|
|
116
118
|
end
|
|
117
|
-
end
|
|
119
|
+
end
|
data/lib/radfish/http_client.rb
CHANGED
|
@@ -24,14 +24,29 @@ module Radfish
|
|
|
24
24
|
IDEMPOTENT_METHODS = [:get, :head, :put, :delete].freeze
|
|
25
25
|
RETRY_STATUSES = [408, 429, 500, 502, 503, 504].freeze
|
|
26
26
|
|
|
27
|
+
# Secrets stripped from the debug log. Redfish session payloads use
|
|
28
|
+
# "Password" where other calls use "password", and the session token comes
|
|
29
|
+
# back as a header, so match either case and both header shapes.
|
|
30
|
+
LOG_FILTERS = [
|
|
31
|
+
# Faraday logs a header as `Authorization: "Basic dXNlcjpwdw=="` and a
|
|
32
|
+
# headers hash as `"Authorization"=>"Basic ..."`, so the quote sits
|
|
33
|
+
# between the colon and the scheme. Match both shapes, and any scheme,
|
|
34
|
+
# keeping the scheme itself visible because it is useful and not secret.
|
|
35
|
+
[/(Authorization"?\s*(?:=>|:)\s*"?)((?:Basic|Bearer|Digest)\s+)?[^"\n]+/i, '\1\2[FILTERED]'],
|
|
36
|
+
[/(Password"=>"?)([^,"]+)/i, '\1[FILTERED]'],
|
|
37
|
+
[/("password"\s*:\s*")([^"]+)/i, '\1[FILTERED]'],
|
|
38
|
+
[/((?:X-Auth-Token)"?\s*(?:=>|:)\s*"?)([^",\n]+)/i, '\1[FILTERED]']
|
|
39
|
+
].freeze
|
|
40
|
+
|
|
27
41
|
attr_reader :host, :port, :use_ssl, :verify_ssl
|
|
28
42
|
attr_accessor :username, :password, :verbosity, :retry_count, :retry_delay,
|
|
29
|
-
:retry_methods, :max_redirects
|
|
43
|
+
:retry_methods, :max_redirects, :log_device
|
|
30
44
|
|
|
31
45
|
def initialize(host:, port: 443, use_ssl: true, verify_ssl: false,
|
|
32
46
|
username: nil, password: nil, verbosity: 0,
|
|
33
47
|
retry_count: 3, retry_delay: 1,
|
|
34
|
-
retry_methods: IDEMPOTENT_METHODS, max_redirects: 3,
|
|
48
|
+
retry_methods: IDEMPOTENT_METHODS, max_redirects: 3,
|
|
49
|
+
log_device: STDOUT, **options)
|
|
35
50
|
@host = host
|
|
36
51
|
@port = port
|
|
37
52
|
@use_ssl = use_ssl
|
|
@@ -43,9 +58,16 @@ module Radfish
|
|
|
43
58
|
@retry_delay = retry_delay
|
|
44
59
|
@retry_methods = retry_methods
|
|
45
60
|
@max_redirects = max_redirects
|
|
61
|
+
@log_device = log_device
|
|
46
62
|
@options = options
|
|
47
63
|
end
|
|
48
64
|
|
|
65
|
+
# The Faraday logger applies LOG_FILTERS, but our own debug lines bypass it,
|
|
66
|
+
# so anything we print that can carry a header or a body goes through here.
|
|
67
|
+
def self.scrub(text)
|
|
68
|
+
LOG_FILTERS.reduce(text.to_s) { |t, (pattern, replacement)| t.gsub(pattern, replacement) }
|
|
69
|
+
end
|
|
70
|
+
|
|
49
71
|
def base_url
|
|
50
72
|
protocol = use_ssl ? 'https' : 'http'
|
|
51
73
|
"#{protocol}://#{host}:#{port}"
|
|
@@ -91,7 +113,7 @@ module Radfish
|
|
|
91
113
|
response = conn.send(method) do |req|
|
|
92
114
|
debug "Setting request URL: #{path}", 3, :cyan
|
|
93
115
|
req.url path
|
|
94
|
-
debug "Merging headers: #{headers}", 3, :cyan
|
|
116
|
+
debug "Merging headers: #{self.class.scrub(headers)}", 3, :cyan
|
|
95
117
|
req.headers.merge!(headers)
|
|
96
118
|
req.body = body if body
|
|
97
119
|
|
|
@@ -152,6 +174,10 @@ module Radfish
|
|
|
152
174
|
end
|
|
153
175
|
|
|
154
176
|
raise Radfish::ConnectionError, "SSL error connecting to #{host}: #{e.message}"
|
|
177
|
+
rescue Faraday::Error => e
|
|
178
|
+
# HttpClient is the seam: callers speak Radfish errors, not Faraday ones.
|
|
179
|
+
debug "HTTP request failed: #{e.class} - #{e.message}", 1, :red
|
|
180
|
+
raise Radfish::Error, "Request to #{host} failed: #{e.message}"
|
|
155
181
|
rescue => e
|
|
156
182
|
debug "HTTP request failed: #{e.class} - #{e.message}", 1, :red
|
|
157
183
|
debug "Exception backtrace: #{e.backtrace.first(5).join("\n")}", 1, :red
|
|
@@ -260,10 +286,8 @@ module Radfish
|
|
|
260
286
|
|
|
261
287
|
# Add logging if verbose
|
|
262
288
|
if verbosity >= 2
|
|
263
|
-
faraday.response :logger, Logger.new(
|
|
264
|
-
logger.filter(
|
|
265
|
-
logger.filter(/(Password"=>"?)([^,"]+)/, '\1[FILTERED]')
|
|
266
|
-
logger.filter(/("password":\s*")([^"]+)/, '\1[FILTERED]')
|
|
289
|
+
faraday.response :logger, Logger.new(log_device), { bodies: verbosity >= 3 } do |logger|
|
|
290
|
+
LOG_FILTERS.each { |pattern, replacement| logger.filter(pattern, replacement) }
|
|
267
291
|
end
|
|
268
292
|
end
|
|
269
293
|
|
data/lib/radfish/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: radfish
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jonathan Siegel
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-09-
|
|
11
|
+
date: 2026-09-12 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: thor
|