-
Notifications
You must be signed in to change notification settings - Fork 402
/
client.go
132 lines (106 loc) · 3.72 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
// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
package checker
import (
"bytes"
"context"
"encoding/json"
"io"
"net/http"
"reflect"
"strings"
"time"
"github.com/spacemonkeygo/monkit/v3"
"github.com/zeebo/errs"
"golang.org/x/text/cases"
"golang.org/x/text/language"
"storj.io/private/version"
)
var (
mon = monkit.Package()
// Error is the error class for version checker client errors.
Error = errs.Class("version checker client")
)
// ClientConfig is the config struct for the version control client.
type ClientConfig struct {
ServerAddress string `help:"server address to check its version against" default:"https://version.storj.io"`
RequestTimeout time.Duration `help:"Request timeout for version checks" default:"0h1m0s"`
}
// Client defines helper methods for using version control server response data.
//
// architecture: Client
type Client struct {
config ClientConfig
}
// New constructs a new verson control server client.
func New(config ClientConfig) *Client {
return &Client{
config: config,
}
}
// All handles the HTTP request to gather the latest version information.
func (client *Client) All(ctx context.Context) (ver version.AllowedVersions, err error) {
defer mon.Task()(&ctx)(&err)
// Tune Client to have a custom Timeout (reduces hanging software)
httpClient := http.Client{
Timeout: client.config.RequestTimeout,
}
// New Request that used the passed in context
req, err := http.NewRequestWithContext(ctx, http.MethodGet, client.config.ServerAddress, nil)
if err != nil {
return version.AllowedVersions{}, Error.Wrap(err)
}
resp, err := httpClient.Do(req)
if err != nil {
return version.AllowedVersions{}, Error.Wrap(err)
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return version.AllowedVersions{}, Error.Wrap(err)
}
defer func() { _ = resp.Body.Close() }()
if resp.StatusCode != http.StatusOK {
return version.AllowedVersions{}, Error.New("non-success http status code: %d; body: %s\n", resp.StatusCode, body)
}
err = json.NewDecoder(bytes.NewReader(body)).Decode(&ver)
return ver, Error.Wrap(err)
}
// OldMinimum returns the version with the given name at the root-level of the version control response.
// NB: This will be deprecated eventually in favor of what is currently the `processes` root-level object.
func (client *Client) OldMinimum(ctx context.Context, serviceName string) (ver version.OldSemVer, err error) {
defer mon.Task()(&ctx, serviceName)(&err)
versions, err := client.All(ctx)
if err != nil {
return version.OldSemVer{}, Error.Wrap(err)
}
r := reflect.ValueOf(&versions)
f := reflect.Indirect(r).FieldByName(serviceName).Interface()
result, ok := f.(version.OldSemVer)
if !ok {
return version.OldSemVer{}, Error.New("invalid process name: %s", serviceName)
}
return result, nil
}
// Process returns the version info for the named process from the version control server response.
func (client *Client) Process(ctx context.Context, processName string) (process version.Process, err error) {
defer mon.Task()(&ctx, processName)(&err)
versions, err := client.All(ctx)
if err != nil {
return version.Process{}, Error.Wrap(err)
}
processesValue := reflect.ValueOf(versions.Processes)
field := processesValue.FieldByName(kebabToPascal(processName))
processNameErr := Error.New("invalid process name: %s\n", processName)
if field == (reflect.Value{}) {
return version.Process{}, processNameErr
}
process, ok := field.Interface().(version.Process)
if !ok {
return version.Process{}, processNameErr
}
return process, nil
}
// kebabToPascal converts `alpha-beta` to `AlphaBeta`.
func kebabToPascal(str string) string {
return strings.ReplaceAll(cases.Title(language.Und, cases.NoLower).String(str), "-", "")
}