Skip to content
This repository was archived by the owner on Aug 30, 2024. It is now read-only.

Commit 0801cfc

Browse files
committed
internal/cmd/update.go: use /api/private/version instead of sniffing HTTP headers
1 parent dcfeec1 commit 0801cfc

File tree

2 files changed

+36
-18
lines changed

2 files changed

+36
-18
lines changed

internal/cmd/update.go

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ import (
77
"bytes"
88
"compress/gzip"
99
"context"
10+
"encoding/json"
1011
"fmt"
1112
"io"
13+
"io/ioutil"
1214
"net/http"
1315
"net/url"
1416
"os"
@@ -307,21 +309,29 @@ func getCoderConfigURL() (*url.URL, error) {
307309
// XXX: coder.Client requires an API key, but we may not be logged into the coder instance for which we
308310
// want to determine the version. We don't need an API key to sniff the version header.
309311
func getAPIVersionUnauthed(client getter, baseURL url.URL) (*semver.Version, error) {
310-
baseURL.Path = path.Join(baseURL.Path, "/api")
312+
baseURL.Path = path.Join(baseURL.Path, "/api/private/version")
311313
resp, err := client.Get(baseURL.String())
312314
if err != nil {
313315
return nil, xerrors.Errorf("get %s: %w", baseURL.String(), err)
314316
}
315317
defer resp.Body.Close()
316318

317-
versionHdr := resp.Header.Get("coder-version")
318-
if versionHdr == "" {
319-
return nil, xerrors.Errorf("URL %s response missing coder-version header", baseURL.String())
319+
ver := struct {
320+
Version string `json:"version"`
321+
}{}
322+
323+
body, err := ioutil.ReadAll(resp.Body)
324+
if err != nil {
325+
return nil, xerrors.Errorf("read response body: %w", err)
326+
}
327+
328+
if err := json.Unmarshal(body, &ver); err != nil {
329+
return nil, xerrors.Errorf("parse version response: %w", err)
320330
}
321331

322-
version, err := semver.StrictNewVersion(versionHdr)
332+
version, err := semver.StrictNewVersion(ver.Version)
323333
if err != nil {
324-
return nil, xerrors.Errorf("parsing coder-version header: %w", err)
334+
return nil, xerrors.Errorf("parsing coder version: %w", err)
325335
}
326336

327337
return version, nil

internal/cmd/update_test.go

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"bytes"
55
"context"
66
"encoding/base64"
7+
"fmt"
78
"io/fs"
89
"io/ioutil"
910
"net"
@@ -28,6 +29,12 @@ const (
2829
fakeReleaseURLWindows = "https://github.com/cdr/coder-cli/releases/download/v1.23.4-rc.5/coder-cli-windows-amd64.zip"
2930
)
3031

32+
var (
33+
apiPrivateVersionURL = fakeCoderURL + "/api/private/version"
34+
fakeNewVersionJson = fmt.Sprintf(`{"version":%q}`, fakeNewVersion)
35+
fakeOldVersionJson = fmt.Sprintf(`{"version":%q}`, fakeOldVersion)
36+
)
37+
3138
func Test_updater_run(t *testing.T) {
3239
t.Parallel()
3340

@@ -84,7 +91,7 @@ func Test_updater_run(t *testing.T) {
8491

8592
run(t, "update coder - noop", func(t *testing.T, p *params) {
8693
fakeFile(t, p.Fakefs, fakeExePathLinux, 0755, fakeNewVersion)
87-
p.HttpClient.M[fakeCoderURL+"/api"] = newFakeGetterResponse([]byte{}, 401, variadicS("coder-version: "+fakeNewVersion), nil)
94+
p.HttpClient.M[apiPrivateVersionURL] = newFakeGetterResponse([]byte(fakeNewVersionJson), 200, variadicS(), nil)
8895
p.VersionF = func() string { return fakeNewVersion }
8996
u := fromParams(p)
9097
assertFileContent(t, p.Fakefs, fakeExePathLinux, fakeNewVersion)
@@ -95,7 +102,7 @@ func Test_updater_run(t *testing.T) {
95102

96103
run(t, "update coder - old to new", func(t *testing.T, p *params) {
97104
fakeFile(t, p.Fakefs, fakeExePathLinux, 0755, fakeOldVersion)
98-
p.HttpClient.M[fakeCoderURL+"/api"] = newFakeGetterResponse([]byte{}, 401, variadicS("coder-version: "+fakeNewVersion), nil)
105+
p.HttpClient.M[apiPrivateVersionURL] = newFakeGetterResponse([]byte(fakeNewVersionJson), 200, variadicS(), nil)
99106
p.HttpClient.M[fakeReleaseURLLinux] = newFakeGetterResponse(fakeValidTgzBytes, 200, variadicS(), nil)
100107
p.VersionF = func() string { return fakeOldVersion }
101108
p.ConfirmF = fakeConfirmYes
@@ -109,7 +116,7 @@ func Test_updater_run(t *testing.T) {
109116
run(t, "update coder - old to new - binary renamed", func(t *testing.T, p *params) {
110117
p.ExecutablePath = "/home/user/bin/coder-cli"
111118
fakeFile(t, p.Fakefs, p.ExecutablePath, 0755, fakeOldVersion)
112-
p.HttpClient.M[fakeCoderURL+"/api"] = newFakeGetterResponse([]byte{}, 401, variadicS("coder-version: "+fakeNewVersion), nil)
119+
p.HttpClient.M[apiPrivateVersionURL] = newFakeGetterResponse([]byte(fakeNewVersionJson), 200, variadicS(), nil)
113120
p.HttpClient.M[fakeReleaseURLLinux] = newFakeGetterResponse(fakeValidTgzBytes, 200, variadicS(), nil)
114121
p.VersionF = func() string { return fakeOldVersion }
115122
p.ConfirmF = fakeConfirmYes
@@ -124,7 +131,7 @@ func Test_updater_run(t *testing.T) {
124131
p.OsF = func() string { return "windows" }
125132
p.ExecutablePath = fakeExePathWindows
126133
fakeFile(t, p.Fakefs, fakeExePathWindows, 0755, fakeOldVersion)
127-
p.HttpClient.M[fakeCoderURL+"/api"] = newFakeGetterResponse([]byte{}, 401, variadicS("coder-version: "+fakeNewVersion), nil)
134+
p.HttpClient.M[apiPrivateVersionURL] = newFakeGetterResponse([]byte(fakeNewVersionJson), 200, variadicS(), nil)
128135
p.HttpClient.M[fakeReleaseURLWindows] = newFakeGetterResponse(fakeValidZipBytes, 200, variadicS(), nil)
129136
p.VersionF = func() string { return fakeOldVersion }
130137
p.ConfirmF = fakeConfirmYes
@@ -137,7 +144,7 @@ func Test_updater_run(t *testing.T) {
137144

138145
run(t, "update coder - old to new forced", func(t *testing.T, p *params) {
139146
fakeFile(t, p.Fakefs, fakeExePathLinux, 0755, fakeOldVersion)
140-
p.HttpClient.M[fakeCoderURL+"/api"] = newFakeGetterResponse([]byte{}, 401, variadicS("coder-version: "+fakeNewVersion), nil)
147+
p.HttpClient.M[apiPrivateVersionURL] = newFakeGetterResponse([]byte(fakeNewVersionJson), 200, variadicS(), nil)
141148
p.HttpClient.M[fakeReleaseURLLinux] = newFakeGetterResponse(fakeValidTgzBytes, 200, variadicS(), nil)
142149
p.VersionF = func() string { return fakeOldVersion }
143150
u := fromParams(p)
@@ -149,7 +156,7 @@ func Test_updater_run(t *testing.T) {
149156

150157
run(t, "update coder - user cancelled", func(t *testing.T, p *params) {
151158
fakeFile(t, p.Fakefs, fakeExePathLinux, 0755, fakeOldVersion)
152-
p.HttpClient.M[fakeCoderURL+"/api"] = newFakeGetterResponse([]byte{}, 401, variadicS("coder-version: "+fakeNewVersion), nil)
159+
p.HttpClient.M[apiPrivateVersionURL] = newFakeGetterResponse([]byte(fakeNewVersionJson), 200, variadicS(), nil)
153160
p.VersionF = func() string { return fakeOldVersion }
154161
p.ConfirmF = fakeConfirmNo
155162
u := fromParams(p)
@@ -186,7 +193,7 @@ func Test_updater_run(t *testing.T) {
186193

187194
run(t, "update coder - fetch api version failure", func(t *testing.T, p *params) {
188195
fakeFile(t, p.Fakefs, fakeExePathLinux, 0755, fakeOldVersion)
189-
p.HttpClient.M[fakeCoderURL+"/api"] = newFakeGetterResponse([]byte{}, 401, variadicS(), net.ErrClosed)
196+
p.HttpClient.M[apiPrivateVersionURL] = newFakeGetterResponse([]byte{}, 401, variadicS(), net.ErrClosed)
190197
p.VersionF = func() string { return fakeOldVersion }
191198
u := fromParams(p)
192199
assertFileContent(t, p.Fakefs, fakeExePathLinux, fakeOldVersion)
@@ -197,7 +204,7 @@ func Test_updater_run(t *testing.T) {
197204

198205
run(t, "update coder - failed to fetch URL", func(t *testing.T, p *params) {
199206
fakeFile(t, p.Fakefs, fakeExePathLinux, 0755, fakeOldVersion)
200-
p.HttpClient.M[fakeCoderURL+"/api"] = newFakeGetterResponse([]byte{}, 401, variadicS("coder-version: "+fakeNewVersion), nil)
207+
p.HttpClient.M[apiPrivateVersionURL] = newFakeGetterResponse([]byte(fakeNewVersionJson), 200, variadicS(), nil)
201208
p.HttpClient.M[fakeReleaseURLLinux] = newFakeGetterResponse([]byte{}, 0, variadicS(), net.ErrClosed)
202209
p.VersionF = func() string { return fakeOldVersion }
203210
p.ConfirmF = fakeConfirmYes
@@ -210,7 +217,7 @@ func Test_updater_run(t *testing.T) {
210217

211218
run(t, "update coder - release URL 404", func(t *testing.T, p *params) {
212219
fakeFile(t, p.Fakefs, fakeExePathLinux, 0755, fakeOldVersion)
213-
p.HttpClient.M[fakeCoderURL+"/api"] = newFakeGetterResponse([]byte{}, 401, variadicS("coder-version: "+fakeNewVersion), nil)
220+
p.HttpClient.M[apiPrivateVersionURL] = newFakeGetterResponse([]byte(fakeNewVersionJson), 200, variadicS(), nil)
214221
p.HttpClient.M[fakeReleaseURLLinux] = newFakeGetterResponse([]byte{}, 404, variadicS(), nil)
215222
p.VersionF = func() string { return fakeOldVersion }
216223
p.ConfirmF = fakeConfirmYes
@@ -223,7 +230,7 @@ func Test_updater_run(t *testing.T) {
223230

224231
run(t, "update coder - invalid tgz archive", func(t *testing.T, p *params) {
225232
fakeFile(t, p.Fakefs, fakeExePathLinux, 0755, fakeOldVersion)
226-
p.HttpClient.M[fakeCoderURL+"/api"] = newFakeGetterResponse([]byte{}, 401, variadicS("coder-version: "+fakeNewVersion), nil)
233+
p.HttpClient.M[apiPrivateVersionURL] = newFakeGetterResponse([]byte(fakeNewVersionJson), 200, variadicS(), nil)
227234
p.HttpClient.M[fakeReleaseURLLinux] = newFakeGetterResponse([]byte{}, 200, variadicS(), nil)
228235
p.VersionF = func() string { return fakeOldVersion }
229236
p.ConfirmF = fakeConfirmYes
@@ -238,7 +245,7 @@ func Test_updater_run(t *testing.T) {
238245
p.OsF = func() string { return "windows" }
239246
p.ExecutablePath = fakeExePathWindows
240247
fakeFile(t, p.Fakefs, fakeExePathWindows, 0755, fakeOldVersion)
241-
p.HttpClient.M[fakeCoderURL+"/api"] = newFakeGetterResponse([]byte{}, 401, variadicS("coder-version: "+fakeNewVersion), nil)
248+
p.HttpClient.M[apiPrivateVersionURL] = newFakeGetterResponse([]byte(fakeNewVersionJson), 200, variadicS(), nil)
242249
p.HttpClient.M[fakeReleaseURLWindows] = newFakeGetterResponse([]byte{}, 200, variadicS(), nil)
243250
p.VersionF = func() string { return fakeOldVersion }
244251
p.ConfirmF = fakeConfirmYes
@@ -253,7 +260,7 @@ func Test_updater_run(t *testing.T) {
253260
rwfs := p.Fakefs
254261
p.Fakefs = afero.NewReadOnlyFs(rwfs)
255262
fakeFile(t, rwfs, fakeExePathLinux, 0755, fakeOldVersion)
256-
p.HttpClient.M[fakeCoderURL+"/api"] = newFakeGetterResponse([]byte{}, 401, variadicS("coder-version: "+fakeNewVersion), nil)
263+
p.HttpClient.M[apiPrivateVersionURL] = newFakeGetterResponse([]byte(fakeNewVersionJson), 200, variadicS(), nil)
257264
p.HttpClient.M[fakeReleaseURLLinux] = newFakeGetterResponse(fakeValidTgzBytes, 200, variadicS(), nil)
258265
p.VersionF = func() string { return fakeOldVersion }
259266
p.ConfirmF = fakeConfirmYes
@@ -280,6 +287,7 @@ func newFakeGetter(t *testing.T) *fakeGetter {
280287

281288
// Get returns the configured response for url. If no response configured, test fails immediately.
282289
func (f *fakeGetter) Get(url string) (*http.Response, error) {
290+
f.T.Helper()
283291
val, ok := f.M[url]
284292
if !ok {
285293
f.T.Errorf("unhandled url: %s", url)

0 commit comments

Comments
 (0)