-
Notifications
You must be signed in to change notification settings - Fork 14
/
Copy pathrunstatus.go
130 lines (108 loc) · 3.08 KB
/
runstatus.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
package egoscale
import (
"context"
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"strings"
"time"
)
// RunstatusValidationErrorResponse represents an error in the API
type RunstatusValidationErrorResponse map[string][]string
// RunstatusErrorResponse represents the default errors
type RunstatusErrorResponse struct {
Detail string `json:"detail"`
}
// runstatusPagesURL is the only URL that cannot be guessed
const runstatusPagesURL = "/pages"
// Error formats the DNSerror into a string
func (req RunstatusErrorResponse) Error() string {
return fmt.Sprintf("Runstatus error: %s", req.Detail)
}
// Error formats the DNSerror into a string
func (req RunstatusValidationErrorResponse) Error() string {
if len(req) > 0 {
errs := []string{}
for name, ss := range req {
if len(ss) > 0 {
errs = append(errs, fmt.Sprintf("%s: %s", name, strings.Join(ss, ", ")))
}
}
return fmt.Sprintf("Runstatus error: %s", strings.Join(errs, "; "))
}
return "Runstatus error"
}
func (client *Client) runstatusRequest(ctx context.Context, uri string, structParam interface{}, method string) (json.RawMessage, error) {
reqURL, err := url.Parse(uri)
if err != nil {
return nil, err
}
if reqURL.Scheme == "" {
return nil, fmt.Errorf("only absolute URI are considered valid, got %q", uri)
}
var params string
if structParam != nil {
m, err := json.Marshal(structParam)
if err != nil {
return nil, err
}
params = string(m)
}
req, err := http.NewRequest(method, reqURL.String(), strings.NewReader(params))
if err != nil {
return nil, err
}
time := time.Now().Local().Format("2006-01-02T15:04:05-0700")
payload := fmt.Sprintf("%s%s%s", req.URL.String(), time, params)
mac := hmac.New(sha256.New, []byte(client.apiSecret))
_, err = mac.Write([]byte(payload))
if err != nil {
return nil, err
}
signature := hex.EncodeToString(mac.Sum(nil))
var hdr = make(http.Header)
hdr.Add("Authorization", fmt.Sprintf("Exoscale-HMAC-SHA256 %s:%s", client.APIKey, signature))
hdr.Add("Exoscale-Date", time)
hdr.Add("Accept", "application/json")
if params != "" {
hdr.Add("Content-Type", "application/json")
}
req.Header = hdr
req = req.WithContext(ctx)
resp, err := client.HTTPClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close() // nolint: errcheck
if resp.StatusCode == 204 {
if method != "DELETE" {
return nil, fmt.Errorf("only DELETE is expected to produce 204, was %q", method)
}
return nil, nil
}
contentType := resp.Header.Get("content-type")
if !strings.Contains(contentType, "application/json") {
return nil, fmt.Errorf(`response %d content-type expected to be "application/json", got %q`, resp.StatusCode, contentType)
}
b, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
if resp.StatusCode >= 400 {
rerr := new(RunstatusValidationErrorResponse)
if err := json.Unmarshal(b, rerr); err == nil {
return nil, rerr
}
rverr := new(RunstatusErrorResponse)
if err := json.Unmarshal(b, rverr); err != nil {
return nil, err
}
return nil, rverr
}
return b, nil
}