-
Notifications
You must be signed in to change notification settings - Fork 19
/
Copy pathclient.go
133 lines (113 loc) · 3.33 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
package client
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
)
type UpgradeChecker struct {
Address string
UpgradeRequester UpgradeRequester
DefaultRequestInterval time.Duration
stopCh chan struct{}
}
type UpgradeRequester interface {
GetCurrentVersion() string
GetExtraInfo() map[string]string
ProcessUpgradeResponse(response *CheckUpgradeResponse, err error)
}
type Version struct {
Name string `json:"name"` // must be in semantic versioning
ReleaseDate string `json:"releaseDate"`
MinUpgradableVersion string `json:"minUpgradableVersion"`
Tags []string `json:"tags"`
ExtraInfo map[string]string `json:"extraInfo"`
}
type CheckUpgradeRequest struct {
AppVersion string `json:"appVersion"`
ExtraTagInfo map[string]string `json:"extraTagInfo"`
ExtraFieldInfo map[string]interface{} `json:"extraFieldInfo"`
// Deprecated: replaced by ExtraTagInfo
ExtraInfo map[string]string `json:"extraInfo"`
}
type CheckUpgradeResponse struct {
Versions []Version `json:"versions"`
RequestIntervalInMinutes int `json:"requestIntervalInMinutes"`
}
func NewUpgradeChecker(address string, upgradeRequester UpgradeRequester) *UpgradeChecker {
return &UpgradeChecker{
Address: address,
UpgradeRequester: upgradeRequester,
DefaultRequestInterval: 1 * time.Hour,
stopCh: make(chan struct{}),
}
}
func (c *UpgradeChecker) Start() {
go c.run()
}
func (c *UpgradeChecker) run() {
requestInterval := c.DefaultRequestInterval
doWork := func() {
resp, err := c.CheckUpgrade(c.UpgradeRequester.GetCurrentVersion(), c.UpgradeRequester.GetExtraInfo())
if err == nil && resp.RequestIntervalInMinutes > 0 {
requestInterval = time.Duration(resp.RequestIntervalInMinutes) * time.Minute
}
c.UpgradeRequester.ProcessUpgradeResponse(resp, err)
}
doWork()
for {
select {
case <-time.After(requestInterval):
doWork()
case <-c.stopCh:
return
}
}
}
func (c *UpgradeChecker) Stop() {
select {
case <-c.stopCh:
// stopCh channel is already closed
default:
close(c.stopCh)
}
}
func (c *UpgradeChecker) SetDefaultRequestInterval(interval time.Duration) {
c.DefaultRequestInterval = interval
}
// CheckUpgrade sends a request that contains the current version of the application and any extra information to the Upgrade Responder server.
// Then it parses and return the response
func (c *UpgradeChecker) CheckUpgrade(currentAppVersion string, extraInfo map[string]string) (*CheckUpgradeResponse, error) {
var (
resp CheckUpgradeResponse
content bytes.Buffer
)
req := &CheckUpgradeRequest{
AppVersion: currentAppVersion,
ExtraInfo: extraInfo,
}
if err := json.NewEncoder(&content).Encode(req); err != nil {
return nil, err
}
r, err := http.Post(c.Address, "application/json", &content)
if err != nil {
return nil, err
}
defer r.Body.Close()
if r.StatusCode != http.StatusOK {
message := ""
messageBytes, err := io.ReadAll(r.Body)
if err != nil {
message = err.Error()
} else {
message = string(messageBytes)
}
return nil, fmt.Errorf("query return status code %v, message %v", r.StatusCode, message)
}
if err := json.NewDecoder(r.Body).Decode(&resp); err != nil {
return nil, err
}
return &resp, nil
}