train-rest 0.2.0 → 0.3.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/README.md +77 -5
- data/lib/train-rest/auth_handler/anonymous.rb +1 -1
- data/lib/train-rest/auth_handler/basic.rb +6 -3
- data/lib/train-rest/auth_handler/redfish.rb +14 -4
- data/lib/train-rest/connection.rb +51 -12
- data/lib/train-rest/transport.rb +26 -1
- data/lib/train-rest/version.rb +1 -1
- data/train-rest.gemspec +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: 45c7cbd2b28a41c2eba5d219545d4ff7e0bfbaf6305f239630352ce463bf8424
|
|
4
|
+
data.tar.gz: 6e5de06f898f95a2a2c519ac45bae42c797e39840e2aec0c80bb6204d5e69591
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 48c46e55522807433595d89e5bf414a3d487c9167b7f8fd365a8a64645789fd533fc614a835b319f8d8e6590d907b01c1fbf864ea80fc735d333d2961c065ddf
|
|
7
|
+
data.tar.gz: 9e24f8c14aa8000541aade240e46d0aaaac67a70a337672f821f9760336da98f482af80337e963ef64c218637f4ed36b059744c8dcc1a2d21cd08d73b5e5154d
|
data/README.md
CHANGED
|
@@ -20,7 +20,7 @@ rake install:local
|
|
|
20
20
|
| Option | Explanation | Default |
|
|
21
21
|
| -------------------- | --------------------------------------- | ----------- |
|
|
22
22
|
| `endpoint` | Endpoint of the REST API | _required_ |
|
|
23
|
-
| `
|
|
23
|
+
| `verify_ssl` | Check certificate and chain | true |
|
|
24
24
|
| `auth_type` | Authentication type | `anonymous` |
|
|
25
25
|
| `debug_rest` | Enable debugging of HTTP traffic | false |
|
|
26
26
|
| `logger` | Alternative logging class | |
|
|
@@ -60,6 +60,9 @@ The Redfish standard is defined in <http://www.dmtf.org/standards/redfish> and
|
|
|
60
60
|
this handler does initial login, reuses the received session and logs out when
|
|
61
61
|
closing the transport cleanly.
|
|
62
62
|
|
|
63
|
+
Known vendors which implement RedFish based management for their systems include
|
|
64
|
+
HPE, Dell, IBM, SuperMicro, Lenovo, Huawei and others.
|
|
65
|
+
|
|
63
66
|
## Debugging and use in Chef
|
|
64
67
|
|
|
65
68
|
You can activate debugging by setting the `debug_rest` flag to `true'. Please
|
|
@@ -77,6 +80,46 @@ train = Train.create('rest', {
|
|
|
77
80
|
})
|
|
78
81
|
```
|
|
79
82
|
|
|
83
|
+
## Request Methods
|
|
84
|
+
|
|
85
|
+
This transport does not implement the `run_command` method, as there is no
|
|
86
|
+
line-based protocol to execute commands against. Instead, it implements its own
|
|
87
|
+
custom methods which suit REST interfaces. Trying to call this method will
|
|
88
|
+
throw an Exception.
|
|
89
|
+
|
|
90
|
+
### Generic Request
|
|
91
|
+
|
|
92
|
+
The `request` methods allows to send free-form requests against any defined or
|
|
93
|
+
custom methods.
|
|
94
|
+
|
|
95
|
+
`request(path, method = :get, request_parameters: {}, data: nil, headers: {},
|
|
96
|
+
json_processing: true)`
|
|
97
|
+
|
|
98
|
+
- `path`: The path to request, which will be appended to the `endpoint`
|
|
99
|
+
- `method`: The HTTP method in Ruby Symbol syntax
|
|
100
|
+
- `request_parameters`: A hash of parameters to the `rest-client` request
|
|
101
|
+
method for additional settings
|
|
102
|
+
- `data`: Data for actions like `:post` or `:put`. Not all methods accept
|
|
103
|
+
a data body.
|
|
104
|
+
- `headers`: Additional headers for the request
|
|
105
|
+
- `json_processing`: If the response is a JSON and you want to receive a
|
|
106
|
+
processed Hash/Array instead of text
|
|
107
|
+
|
|
108
|
+
For `request_parameters` and `headers`, there is data mixed in to add
|
|
109
|
+
authenticator responses, JSON processing etc. Please check the
|
|
110
|
+
implementation in `connection.rb` for details.
|
|
111
|
+
|
|
112
|
+
### Convenience Methods
|
|
113
|
+
|
|
114
|
+
Simplified wrappers are generated for the most common request types:
|
|
115
|
+
|
|
116
|
+
- `delete(path, request_parameters: {}, headers: {}, json_processing: true)`
|
|
117
|
+
- `head(path, request_parameters: {}, headers: {}, json_processing: true)`
|
|
118
|
+
- `get(path, request_parameters: {}, headers: {}, json_processing: true)`
|
|
119
|
+
- `post(path, request_parameters: {}, data: nil, headers: {}, json_processing: true)`
|
|
120
|
+
- `put(path, request_parameters: {}, data: nil, headers: {}, json_processing: true)`
|
|
121
|
+
- `patch(path, request_parameters: {}, data: nil, headers: {}, json_processing: true)`
|
|
122
|
+
|
|
80
123
|
## Example use
|
|
81
124
|
|
|
82
125
|
```ruby
|
|
@@ -119,15 +162,21 @@ conn = train.connection
|
|
|
119
162
|
conn.close
|
|
120
163
|
```
|
|
121
164
|
|
|
122
|
-
Example for logging into a RedFish based system
|
|
165
|
+
Example for logging into a RedFish based system. Please note that the RedFish
|
|
166
|
+
authentication handler will append `redfish/v1` to the endpoint automatically,
|
|
167
|
+
if it is not present.
|
|
168
|
+
|
|
169
|
+
Due to this, you can use RedFish systems either with a base URL like in the
|
|
170
|
+
example below or with a full one. Your own code needs to match the style
|
|
171
|
+
you choose.
|
|
123
172
|
|
|
124
173
|
```ruby
|
|
125
174
|
require 'train-rest'
|
|
126
175
|
|
|
127
176
|
# This will immediately do a login and add headers
|
|
128
177
|
train = Train.create('rest', {
|
|
129
|
-
endpoint: 'https://
|
|
130
|
-
|
|
178
|
+
endpoint: 'https://10.20.30.40',
|
|
179
|
+
verify_ssl: false,
|
|
131
180
|
|
|
132
181
|
auth_type: :redfish,
|
|
133
182
|
username: 'iloadmin',
|
|
@@ -170,5 +219,28 @@ conn.close
|
|
|
170
219
|
1. Run against the defiend targets via Chef Target Mode:
|
|
171
220
|
|
|
172
221
|
```shell
|
|
173
|
-
chef-client --local-mode --target 10.0.0.1 --runlist 'recipe[my-cookbook
|
|
222
|
+
chef-client --local-mode --target 10.0.0.1 --runlist 'recipe[my-cookbook::setup]'
|
|
174
223
|
```
|
|
224
|
+
|
|
225
|
+
## Use with Prerecorded API responses
|
|
226
|
+
|
|
227
|
+
For testing during and after development, not all APIs can be used to verify your solution against.
|
|
228
|
+
The VCR gem offers the possibility to hook into web requests and intercept them to play back canned
|
|
229
|
+
responses.
|
|
230
|
+
|
|
231
|
+
Please read the documentation of the VCR gem on how to record your API and the concepts like
|
|
232
|
+
"cassettes", "libraries" and matchers.
|
|
233
|
+
|
|
234
|
+
The following options are available in train-rest for this:
|
|
235
|
+
|
|
236
|
+
| Option | Explanation | Default |
|
|
237
|
+
| -------------------- | --------------------------------------- | ------------ |
|
|
238
|
+
| `vcr_cassette` | Name of the response file | nil |
|
|
239
|
+
| `vcr_library` | Directory to search responses in | `vcr` |
|
|
240
|
+
| `vcr_match_on` | Elements to match request by | `method uri` |
|
|
241
|
+
| `vcr_record` | Recording mode | `none` |
|
|
242
|
+
| `vcr_hook_into` | Base library for intercepting | `webmock` |
|
|
243
|
+
|
|
244
|
+
VCR will only be required as a Gem and activated, if you supply a cassette name.
|
|
245
|
+
|
|
246
|
+
You can use all these settings in your Chef Target Mode `credentials` file as well.
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
require "base64" unless defined?(Base64)
|
|
2
|
+
|
|
3
|
+
require_relative "../auth_handler"
|
|
2
4
|
|
|
3
5
|
module TrainPlugins
|
|
4
6
|
module Rest
|
|
@@ -11,8 +13,9 @@ module TrainPlugins
|
|
|
11
13
|
|
|
12
14
|
def auth_parameters
|
|
13
15
|
{
|
|
14
|
-
|
|
15
|
-
|
|
16
|
+
headers: {
|
|
17
|
+
"Authorization" => format("Basic %s", Base64.encode64(options[:username] + ":" + options[:password]).chomp),
|
|
18
|
+
},
|
|
16
19
|
}
|
|
17
20
|
end
|
|
18
21
|
end
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
require_relative "../auth_handler
|
|
1
|
+
require_relative "../auth_handler"
|
|
2
2
|
|
|
3
3
|
module TrainPlugins
|
|
4
4
|
module Rest
|
|
@@ -13,7 +13,7 @@ module TrainPlugins
|
|
|
13
13
|
|
|
14
14
|
def login
|
|
15
15
|
response = connection.post(
|
|
16
|
-
|
|
16
|
+
login_url,
|
|
17
17
|
headers: {
|
|
18
18
|
"Content-Type" => "application/json",
|
|
19
19
|
"OData-Version" => "4.0",
|
|
@@ -24,10 +24,11 @@ module TrainPlugins
|
|
|
24
24
|
}
|
|
25
25
|
)
|
|
26
26
|
|
|
27
|
-
raise StandardError.new("Authentication with Redfish failed") unless response.code === 201
|
|
28
|
-
|
|
29
27
|
@session_token = response.headers["x-auth-token"].first
|
|
30
28
|
@logout_url = response.headers["location"].first
|
|
29
|
+
|
|
30
|
+
rescue ::RestClient::RequestFailed => err
|
|
31
|
+
raise StandardError.new("Authentication with Redfish failed: " + err.message)
|
|
31
32
|
end
|
|
32
33
|
|
|
33
34
|
def logout
|
|
@@ -39,6 +40,15 @@ module TrainPlugins
|
|
|
39
40
|
|
|
40
41
|
{ "X-Auth-Token": @session_token }
|
|
41
42
|
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
# Prepend the RedFish base, if not a global setting in the connection URL
|
|
47
|
+
def login_url
|
|
48
|
+
return "SessionService/Sessions/" if options[:endpoint].include?("redfish/v1")
|
|
49
|
+
|
|
50
|
+
"redfish/v1/SessionService/Sessions/"
|
|
51
|
+
end
|
|
42
52
|
end
|
|
43
53
|
end
|
|
44
54
|
end
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
require "json"
|
|
2
|
-
require "ostruct"
|
|
3
|
-
require "uri"
|
|
1
|
+
require "json" unless defined?(JSON)
|
|
2
|
+
require "ostruct" unless defined?(OpenStruct)
|
|
3
|
+
require "uri" unless defined?(URI)
|
|
4
4
|
|
|
5
|
-
require "rest-client"
|
|
6
|
-
require "train"
|
|
5
|
+
require "rest-client" unless defined?(RestClient)
|
|
6
|
+
require "train" unless defined?(Train)
|
|
7
7
|
|
|
8
8
|
module TrainPlugins
|
|
9
9
|
module Rest
|
|
@@ -13,9 +13,41 @@ module TrainPlugins
|
|
|
13
13
|
def initialize(options)
|
|
14
14
|
super(options)
|
|
15
15
|
|
|
16
|
+
# Plugin was called with an URI only
|
|
17
|
+
options[:endpoint] = options[:target].sub("rest://", "https://") unless options[:endpoint]
|
|
18
|
+
|
|
19
|
+
# Accept string (CLI) and boolean (API) options
|
|
20
|
+
options[:verify_ssl] = options[:verify_ssl].to_s == "false" ? false : true
|
|
21
|
+
|
|
22
|
+
setup_vcr
|
|
23
|
+
|
|
16
24
|
connect
|
|
17
25
|
end
|
|
18
26
|
|
|
27
|
+
def setup_vcr
|
|
28
|
+
return unless options[:vcr_cassette]
|
|
29
|
+
|
|
30
|
+
require "vcr" unless defined?(VCR)
|
|
31
|
+
|
|
32
|
+
# TODO: Starts from "/" :(
|
|
33
|
+
library = options[:vcr_library]
|
|
34
|
+
match_on = options[:vcr_match_on].split.map(&:to_sym)
|
|
35
|
+
|
|
36
|
+
VCR.configure do |config|
|
|
37
|
+
config.cassette_library_dir = library
|
|
38
|
+
config.hook_into options[:vcr_hook_into]
|
|
39
|
+
config.default_cassette_options = {
|
|
40
|
+
record: options[:vcr_record].to_sym,
|
|
41
|
+
match_requests_on: match_on,
|
|
42
|
+
}
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
VCR.insert_cassette options[:vcr_cassette]
|
|
46
|
+
rescue LoadError
|
|
47
|
+
logger.fatal "Install the vcr gem to use HTTP(S) playback capability"
|
|
48
|
+
raise
|
|
49
|
+
end
|
|
50
|
+
|
|
19
51
|
def connect
|
|
20
52
|
login if auth_handlers.include? auth_type
|
|
21
53
|
end
|
|
@@ -25,17 +57,19 @@ module TrainPlugins
|
|
|
25
57
|
end
|
|
26
58
|
|
|
27
59
|
def uri
|
|
28
|
-
components = URI
|
|
60
|
+
components = URI(options[:endpoint])
|
|
29
61
|
components.scheme = "rest"
|
|
30
62
|
components.to_s
|
|
31
63
|
end
|
|
32
64
|
|
|
65
|
+
# Allow overwriting to refine the type of REST API
|
|
66
|
+
attr_writer :detected_os
|
|
67
|
+
|
|
33
68
|
def inventory
|
|
34
|
-
# Faking it for Chef Target Mode only
|
|
35
69
|
OpenStruct.new({
|
|
36
|
-
name: "rest",
|
|
70
|
+
name: @detected_os || "rest",
|
|
37
71
|
release: TrainPlugins::Rest::VERSION,
|
|
38
|
-
family_hierarchy:
|
|
72
|
+
family_hierarchy: %w{rest api},
|
|
39
73
|
family: "api",
|
|
40
74
|
platform: "rest",
|
|
41
75
|
platform_version: 0,
|
|
@@ -53,8 +87,8 @@ module TrainPlugins
|
|
|
53
87
|
# User-faced API
|
|
54
88
|
|
|
55
89
|
%i{get post put patch delete head}.each do |method|
|
|
56
|
-
define_method(method) do |path,
|
|
57
|
-
request(path, method,
|
|
90
|
+
define_method(method) do |path, **keywords|
|
|
91
|
+
request(path, method, **keywords)
|
|
58
92
|
end
|
|
59
93
|
end
|
|
60
94
|
|
|
@@ -65,8 +99,9 @@ module TrainPlugins
|
|
|
65
99
|
parameters[:url] = full_url(path)
|
|
66
100
|
|
|
67
101
|
if json_processing
|
|
102
|
+
parameters[:headers]["Accept"] = "application/json"
|
|
68
103
|
parameters[:headers]["Content-Type"] = "application/json"
|
|
69
|
-
parameters[:payload] = JSON.generate(data)
|
|
104
|
+
parameters[:payload] = JSON.generate(data) unless data.nil?
|
|
70
105
|
else
|
|
71
106
|
parameters[:payload] = data
|
|
72
107
|
end
|
|
@@ -139,11 +174,15 @@ module TrainPlugins
|
|
|
139
174
|
end
|
|
140
175
|
|
|
141
176
|
def login
|
|
177
|
+
logger.info format("REST Login via %s authentication handler", auth_type.to_s) unless %i{anonymous basic}.include? auth_type
|
|
178
|
+
|
|
142
179
|
auth_handler.options = options
|
|
143
180
|
auth_handler.login
|
|
144
181
|
end
|
|
145
182
|
|
|
146
183
|
def logout
|
|
184
|
+
logger.info format("REST Logout via %s authentication handler", auth_type.to_s) unless %i{anonymous basic}.include? auth_type
|
|
185
|
+
|
|
147
186
|
auth_handler.logout
|
|
148
187
|
end
|
|
149
188
|
end
|
data/lib/train-rest/transport.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
require "rubygems" unless defined?(Gem)
|
|
2
|
+
|
|
1
3
|
require "train-rest/connection"
|
|
2
4
|
|
|
3
5
|
module TrainPlugins
|
|
@@ -8,7 +10,7 @@ module TrainPlugins
|
|
|
8
10
|
option :endpoint, required: true
|
|
9
11
|
option :verify_ssl, default: true
|
|
10
12
|
option :proxy, default: nil
|
|
11
|
-
option :headers, default:
|
|
13
|
+
option :headers, default: {}
|
|
12
14
|
option :timeout, default: 120
|
|
13
15
|
|
|
14
16
|
option :auth_type, default: :anonymous
|
|
@@ -16,9 +18,32 @@ module TrainPlugins
|
|
|
16
18
|
option :password, default: nil
|
|
17
19
|
option :debug_rest, default: false
|
|
18
20
|
|
|
21
|
+
option :vcr_cassette, default: nil
|
|
22
|
+
option :vcr_library, default: "vcr"
|
|
23
|
+
option :vcr_match_on, default: "method uri"
|
|
24
|
+
option :vcr_record, default: "none"
|
|
25
|
+
option :vcr_hook_into, default: "webmock"
|
|
26
|
+
|
|
19
27
|
def connection(_instance_opts = nil)
|
|
28
|
+
dependency_checks
|
|
29
|
+
|
|
20
30
|
@connection ||= TrainPlugins::Rest::Connection.new(@options)
|
|
21
31
|
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def dependency_checks
|
|
36
|
+
return unless @options[:vcr_cassette]
|
|
37
|
+
|
|
38
|
+
raise Gem::LoadError.new("Install VCR Gem for API playback capability") unless gem_installed?("vcr")
|
|
39
|
+
|
|
40
|
+
stubber = @options[:vcr_hook_into]
|
|
41
|
+
raise Gem::LoadError.new("Install #{stubber} Gem for API playback capability") unless gem_installed?(stubber)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def gem_installed?(name)
|
|
45
|
+
Gem::Specification.find_all_by_name(name).any?
|
|
46
|
+
end
|
|
22
47
|
end
|
|
23
48
|
end
|
|
24
49
|
end
|
data/lib/train-rest/version.rb
CHANGED
data/train-rest.gemspec
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: train-rest
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2
|
|
4
|
+
version: 0.3.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Thomas Heinen
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2021-10-08 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: train
|