forked from stmcginnis/gofish
-
Notifications
You must be signed in to change notification settings - Fork 0
/
client.go
214 lines (172 loc) · 5.41 KB
/
client.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
//
// SPDX-License-Identifier: BSD-3-Clause
//
package gofish
import (
"bytes"
"crypto/tls"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"strings"
"github.com/stmcginnis/gofish/common"
"github.com/stmcginnis/gofish/redfish"
)
const userAgent = "gofish/1.0"
const applicationJSON = "application/json"
// APIClient represents a connection to a Redfish/Swordfish enabled service
// or device.
type APIClient struct {
// Endpoint is the URL of the *fish service
endpoint string
// HTTPClient is for direct http actions
HTTPClient *http.Client
// Service is the ServiceRoot of this Redfish instance
Service *Service
// Auth information saved for later to be able to log out
auth *redfish.AuthToken
}
// ClientConfig holds the settings for establishing a connection.
type ClientConfig struct {
// Endpoint is the URL of the redfish service
Endpoint string
// Username is the optional user name to authenticate with.
Username string
// Password is the password to use for authentication.
Password string
// Insecure controls whether to enforce SSL certificate validity.
Insecure bool
// HTTPClient is the optional client to connect with.
HTTPClient *http.Client
}
// Connect creates a new client connection to a Redfish service.
func Connect(config ClientConfig) (c *APIClient, err error) {
if !strings.HasPrefix(config.Endpoint, "http") {
return c, fmt.Errorf("endpoint must starts with http or https")
}
client := &APIClient{endpoint: config.Endpoint}
if config.HTTPClient == nil {
defaultTransport := http.DefaultTransport.(*http.Transport)
transport := &http.Transport{
Proxy: defaultTransport.Proxy,
DialContext: defaultTransport.DialContext,
MaxIdleConns: defaultTransport.MaxIdleConns,
IdleConnTimeout: defaultTransport.IdleConnTimeout,
ExpectContinueTimeout: defaultTransport.ExpectContinueTimeout,
TLSHandshakeTimeout: defaultTransport.TLSHandshakeTimeout,
TLSClientConfig: &tls.Config{
InsecureSkipVerify: config.Insecure,
},
}
client.HTTPClient = &http.Client{Transport: transport}
} else {
client.HTTPClient = config.HTTPClient
}
if config.Username != "" {
// Authenticate with the service
service, err := ServiceRoot(client)
if err != nil {
return nil, err
}
auth, err := service.CreateSession(config.Username, config.Password)
if err != nil {
return nil, err
}
client.Service = service
client.auth = auth
}
return client, err
}
// ConnectDefault creates an unauthenticated connection to a Redfish service.
func ConnectDefault(endpoint string) (c *APIClient, err error) {
if !strings.HasPrefix(endpoint, "http") {
return c, fmt.Errorf("endpoint must starts with http or https")
}
client := &APIClient{endpoint: endpoint}
client.HTTPClient = &http.Client{}
// Fetch the service root
service, err := ServiceRoot(client)
if err != nil {
return nil, err
}
client.Service = service
return client, err
}
// Get performs a GET request against the Redfish service.
func (c *APIClient) Get(url string) (*http.Response, error) {
relativePath := url
if relativePath == "" {
relativePath = common.DefaultServiceRoot
}
return c.runRequest("GET", relativePath, nil)
}
// Post performs a Post request against the Redfish service.
func (c *APIClient) Post(url string, payload interface{}) (*http.Response, error) {
return c.runRequest("POST", url, payload)
}
// Put performs a Put request against the Redfish service.
func (c *APIClient) Put(url string, payload interface{}) (*http.Response, error) {
return c.runRequest("PUT", url, payload)
}
// Patch performs a Patch request against the Redfish service.
func (c *APIClient) Patch(url string, payload interface{}) (*http.Response, error) {
return c.runRequest("PATCH", url, payload)
}
// Delete performs a Delete request against the Redfish service.
func (c *APIClient) Delete(url string) error {
_, err := c.runRequest("DELETE", url, nil)
return err
}
// runRequest actually performs the REST calls.
func (c *APIClient) runRequest(method string, url string, payload interface{}) (*http.Response, error) {
if url == "" {
return nil, fmt.Errorf("Unable to execute request, no target provided")
}
var payloadBuffer io.ReadSeeker
if payload != nil {
body, err := json.Marshal(payload)
if err != nil {
return nil, err
}
payloadBuffer = bytes.NewReader(body)
}
endpoint := fmt.Sprintf("%s%s", c.endpoint, url)
req, err := http.NewRequest(method, endpoint, payloadBuffer)
if err != nil {
return nil, err
}
// Add common headers
req.Header.Set("User-Agent", userAgent)
req.Header.Set("Accept", applicationJSON)
// Add content info if present
if payload != nil {
req.Header.Set("Content-Type", applicationJSON)
}
// Add auth info if authenticated
if c.auth != nil && c.auth.Token != "" {
req.Header.Set("X-Auth-Token", c.auth.Token)
}
req.Close = true
resp, err := c.HTTPClient.Do(req)
if err != nil {
return nil, err
}
if resp.StatusCode != 200 && resp.StatusCode != 201 && resp.StatusCode != 202 && resp.StatusCode != 204 {
payload, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
defer resp.Body.Close()
return nil, fmt.Errorf("%d: %s", resp.StatusCode, string(payload))
}
return resp, err
}
// Logout will delete any active session. Useful to defer logout when creating
// a new connection.
func (c *APIClient) Logout() {
if c.Service != nil && c.auth != nil {
c.Service.DeleteSession(c.auth.Session)
}
}