1 package version
2
3 import (
4 "context"
5 "encoding/json"
6 "errors"
7 "fmt"
8 "net"
9 "net/http"
10
11 "github.com/linkerd/linkerd2/pkg/util"
12 )
13
14
15
16
17 type Channels struct {
18 array []channelVersion
19 }
20
21 var (
22
23 CheckURL = "https://versioncheck.linkerd.io/version.json"
24 )
25
26
27
28 func NewChannels(channel string) (Channels, error) {
29 cv, err := parseChannelVersion(channel)
30 if err != nil {
31 return Channels{}, err
32 }
33
34 return Channels{
35 array: []channelVersion{cv},
36 }, nil
37 }
38
39
40
41
42
43 func (c Channels) Match(actualVersion string) error {
44 if actualVersion == "" {
45 return errors.New("actual version is empty")
46 }
47
48 if c.Empty() {
49 return errors.New("unable to determine version channel")
50 }
51
52 actual, err := parseChannelVersion(actualVersion)
53 if err != nil {
54 return fmt.Errorf("failed to parse actual version: %w", err)
55 }
56
57 for _, cv := range c.array {
58 if cv.updateChannel() == actual.updateChannel() {
59 return match(cv.String(), actualVersion)
60 }
61 }
62
63 return fmt.Errorf("unsupported version channel: %s", actualVersion)
64 }
65
66
67 func (c Channels) Empty() bool {
68 return len(c.array) == 0
69 }
70
71
72
73 func GetLatestVersions(ctx context.Context, uuid string, source string) (Channels, error) {
74 url := fmt.Sprintf("%s?version=%s&uuid=%s&source=%s", CheckURL, Version, uuid, source)
75 return getLatestVersions(ctx, http.DefaultClient, url)
76 }
77
78 func getLatestVersions(ctx context.Context, client *http.Client, url string) (Channels, error) {
79 req, err := http.NewRequest("GET", url, nil)
80 if err != nil {
81 return Channels{}, err
82 }
83
84 rsp, err := client.Do(req.WithContext(ctx))
85 if err != nil {
86 var dnsError *net.DNSError
87 if errors.As(err, &dnsError) {
88 return Channels{}, fmt.Errorf("failed to resolve version check server: %s", url)
89 }
90 return Channels{}, err
91 }
92 defer rsp.Body.Close()
93
94 if rsp.StatusCode != 200 {
95 return Channels{}, fmt.Errorf("unexpected versioncheck response: %s", rsp.Status)
96 }
97
98 bytes, err := util.ReadAllLimit(rsp.Body, util.MB)
99 if err != nil {
100 return Channels{}, err
101 }
102
103 var versionRsp map[string]string
104 err = json.Unmarshal(bytes, &versionRsp)
105 if err != nil {
106 return Channels{}, err
107 }
108
109 channels := Channels{}
110 for c, v := range versionRsp {
111 cv, err := parseChannelVersion(v)
112 if err != nil {
113 return Channels{}, fmt.Errorf("unexpected versioncheck response: %w", err)
114 }
115
116 if c != cv.updateChannel() {
117 return Channels{}, fmt.Errorf("unexpected versioncheck response: channel in %s does not match %s", cv, c)
118 }
119
120 channels.array = append(channels.array, cv)
121 }
122
123 return channels, nil
124 }
125
View as plain text