-
Notifications
You must be signed in to change notification settings - Fork 10
/
request.go
140 lines (113 loc) · 3.5 KB
/
request.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
package vaultlib
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strings"
"github.com/pkg/errors"
)
type request struct {
Req *http.Request
HTTPClient *http.Client
Headers http.Header
Token string
}
// RawRequest create and execute http request against Vault HTTP API for client.
// Use the client's token for authentication.
//
// Specify http method, Vault path (ie /v1/auth/token/lookup) and optional json payload.
// Return the Vault JSON response .
func (c *Client) RawRequest(method, path string, payload interface{}) (result json.RawMessage, err error) {
if len(method) == 0 || len(path) == 0 {
return result, errors.New("Both method and path must be specified")
}
if !strings.HasPrefix(path, "/") {
path = "/" + path
}
url := c.address.String() + path
req, err := c.newRequest(method, url)
if err != nil {
return result, errors.Wrap(errors.WithStack(err), errInfo())
}
if payload != nil {
if err = req.setJSONBody(payload); err != nil {
return result, errors.Wrap(errors.WithStack(err), errInfo())
}
}
rsp, err := req.executeRaw()
if err != nil {
return rsp, errors.Wrap(errors.WithStack(err), errInfo())
}
return rsp, nil
}
// Returns a ready to execute request
func (c *Client) newRequest(method, url string) (*request, error) {
var err error
req := new(request)
req.Req, err = http.NewRequest(method, url, nil)
if err != nil {
return req, errors.Wrap(errors.WithStack(err), errInfo())
}
req.HTTPClient = c.httpClient
token := c.getTokenID()
req.Req.Header.Set("Content-Type", "application/json")
if token != "" {
req.Req.Header.Set("X-Vault-token", token)
}
if len(c.namespace) > 0 {
req.Req.Header.Set("X-Vault-Namespace", c.namespace)
}
return req, errors.Wrap(errors.WithStack(err), errInfo())
}
// Adds JSON formatted body to request
func (r *request) setJSONBody(val interface{}) error {
buf, err := json.Marshal(val)
if err != nil {
return errors.Wrap(errors.WithStack(err), errInfo())
}
r.Req.Body = ioutil.NopCloser(bytes.NewReader(buf))
return nil
}
// vaultResponse holds the generic json response from Vault server
type vaultResponse struct {
RequestID string `json:"request_id"`
LeaseID string `json:"lease_id"`
Renewable bool `json:"renewable"`
LeaseDuration int `json:"lease_duration"`
Data json.RawMessage `json:"data"`
WrapInfo json.RawMessage `json:"wrap_info"`
Warnings json.RawMessage `json:"warnings"`
Auth json.RawMessage `json:"auth"`
}
// Executes the request, parse the result to vaultResponse
func (r *request) execute() (vaultResponse, error) {
var vaultRsp vaultResponse
res, err := r.executeRaw()
if err != nil {
return vaultRsp, errors.Wrap(errors.WithStack(err), errInfo())
}
jsonErr := json.Unmarshal(res, &vaultRsp)
if jsonErr != nil {
return vaultRsp, errors.Wrap(errors.WithStack(err), errInfo())
}
return vaultRsp, nil
}
// Executes the raw request, does not parse Vault response
func (r *request) executeRaw() ([]byte, error) {
res, err := r.HTTPClient.Do(r.Req)
if err != nil {
return nil, errors.Wrap(errors.WithStack(err), errInfo())
}
defer res.Body.Close()
body, readErr := ioutil.ReadAll(res.Body)
if readErr != nil {
return body, errors.Wrap(errors.WithStack(err), errInfo())
}
if res.StatusCode != http.StatusOK {
httpErr := fmt.Sprintf("Vault http call %v returned %v. Body: %v", r.Req.URL.String(), res.Status, string(body))
return body, errors.New(httpErr)
}
return body, nil
}