Skip to content

Commit 1ef9602

Browse files
authored
feat(coderd): add provisioner build version and api_version on serve (#11369)
* assert provisioner daemon version and api_version in unit tests * add build info in HTTP header, extract codersdk.BuildVersionHeader * add api_version to codersdk.ProvisionerDaemon * testutil.MustString -> testutil.MustRandString
1 parent 9031b49 commit 1ef9602

22 files changed

+97
-34
lines changed

cli/login_test.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"github.com/coder/coder/v2/cli/clitest"
1717
"github.com/coder/coder/v2/cli/cliui"
1818
"github.com/coder/coder/v2/coderd/coderdtest"
19+
"github.com/coder/coder/v2/codersdk"
1920
"github.com/coder/coder/v2/pty/ptytest"
2021
)
2122

@@ -58,7 +59,7 @@ func TestLogin(t *testing.T) {
5859
t.Parallel()
5960

6061
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
61-
w.Header().Set("X-Coder-Build-Version", "something")
62+
w.Header().Set(codersdk.BuildVersionHeader, "something")
6263
w.WriteHeader(http.StatusNotFound)
6364
w.Write([]byte("Not Found"))
6465
}))

coderd/apidoc/docs.go

+3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/apidoc/swagger.json

+3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/coderd.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -564,7 +564,7 @@ func New(options *Options) *API {
564564
// Build-Version is helpful for debugging.
565565
func(next http.Handler) http.Handler {
566566
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
567-
w.Header().Add("X-Coder-Build-Version", buildinfo.Version())
567+
w.Header().Add(codersdk.BuildVersionHeader, buildinfo.Version())
568568
next.ServeHTTP(w, r)
569569
})
570570
},
@@ -1194,7 +1194,7 @@ func (api *API) CreateInMemoryProvisionerDaemon(ctx context.Context, name string
11941194
Tags: provisionersdk.MutateTags(uuid.Nil, nil),
11951195
LastSeenAt: sql.NullTime{Time: dbtime.Now(), Valid: true},
11961196
Version: buildinfo.Version(),
1197-
APIVersion: "1.0",
1197+
APIVersion: provisionersdk.APIVersionCurrent,
11981198
})
11991199
if err != nil {
12001200
return nil, xerrors.Errorf("failed to create in-memory provisioner daemon: %w", err)

coderd/database/dbmem/dbmem.go

+1
Original file line numberDiff line numberDiff line change
@@ -7279,6 +7279,7 @@ func (q *FakeQuerier) UpsertProvisionerDaemon(_ context.Context, arg database.Up
72797279
ReplicaID: uuid.NullUUID{},
72807280
LastSeenAt: arg.LastSeenAt,
72817281
Version: arg.Version,
7282+
APIVersion: arg.APIVersion,
72827283
}
72837284
q.provisionerDaemons = append(q.provisionerDaemons, d)
72847285
return d, nil

coderd/database/dbpurge/dbpurge_test.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ func TestDeleteOldProvisionerDaemons(t *testing.T) {
218218
CreatedAt: now.Add(-14 * 24 * time.Hour),
219219
LastSeenAt: sql.NullTime{Valid: true, Time: now.Add(-7 * 24 * time.Hour).Add(time.Minute)},
220220
Version: "1.0.0",
221-
APIVersion: "1.0",
221+
APIVersion: provisionersdk.APIVersionCurrent,
222222
})
223223
require.NoError(t, err)
224224
_, err = db.UpsertProvisionerDaemon(ctx, database.UpsertProvisionerDaemonParams{
@@ -229,7 +229,7 @@ func TestDeleteOldProvisionerDaemons(t *testing.T) {
229229
CreatedAt: now.Add(-8 * 24 * time.Hour),
230230
LastSeenAt: sql.NullTime{Valid: true, Time: now.Add(-8 * 24 * time.Hour).Add(time.Hour)},
231231
Version: "1.0.0",
232-
APIVersion: "1.0",
232+
APIVersion: provisionersdk.APIVersionCurrent,
233233
})
234234
require.NoError(t, err)
235235
_, err = db.UpsertProvisionerDaemon(ctx, database.UpsertProvisionerDaemonParams{
@@ -242,7 +242,7 @@ func TestDeleteOldProvisionerDaemons(t *testing.T) {
242242
},
243243
CreatedAt: now.Add(-9 * 24 * time.Hour),
244244
Version: "1.0.0",
245-
APIVersion: "1.0",
245+
APIVersion: provisionersdk.APIVersionCurrent,
246246
})
247247
require.NoError(t, err)
248248
_, err = db.UpsertProvisionerDaemon(ctx, database.UpsertProvisionerDaemonParams{
@@ -256,7 +256,7 @@ func TestDeleteOldProvisionerDaemons(t *testing.T) {
256256
CreatedAt: now.Add(-6 * 24 * time.Hour),
257257
LastSeenAt: sql.NullTime{Valid: true, Time: now.Add(-6 * 24 * time.Hour)},
258258
Version: "1.0.0",
259-
APIVersion: "1.0",
259+
APIVersion: provisionersdk.APIVersionCurrent,
260260
})
261261
require.NoError(t, err)
262262

coderd/provisionerdserver/provisionerdserver_test.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"golang.org/x/oauth2"
2525

2626
"cdr.dev/slog/sloggers/slogtest"
27+
"github.com/coder/coder/v2/buildinfo"
2728
"github.com/coder/coder/v2/cli/clibase"
2829
"github.com/coder/coder/v2/coderd/audit"
2930
"github.com/coder/coder/v2/coderd/database"
@@ -1784,8 +1785,8 @@ func setup(t *testing.T, ignoreLogErrors bool, ov *overrides) (proto.DRPCProvisi
17841785
Provisioners: []database.ProvisionerType{database.ProvisionerTypeEcho},
17851786
Tags: database.StringMap{},
17861787
LastSeenAt: sql.NullTime{},
1787-
Version: "",
1788-
APIVersion: "1.0",
1788+
Version: buildinfo.Version(),
1789+
APIVersion: provisionersdk.APIVersionCurrent,
17891790
})
17901791
require.NoError(t, err)
17911792

codersdk/client.go

+3
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ const (
7878

7979
// ProvisionerDaemonPSK contains the authentication pre-shared key for an external provisioner daemon
8080
ProvisionerDaemonPSK = "Coder-Provisioner-Daemon-PSK"
81+
82+
// BuildVersionHeader contains build information of Coder.
83+
BuildVersionHeader = "X-Coder-Build-Version"
8184
)
8285

8386
// loggableMimeTypes is a list of MIME types that are safe to log

codersdk/provisionerdaemons.go

+3
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"golang.org/x/xerrors"
1616
"nhooyr.io/websocket"
1717

18+
"github.com/coder/coder/v2/buildinfo"
1819
"github.com/coder/coder/v2/codersdk/drpc"
1920
"github.com/coder/coder/v2/provisionerd/proto"
2021
"github.com/coder/coder/v2/provisionerd/runner"
@@ -41,6 +42,7 @@ type ProvisionerDaemon struct {
4142
LastSeenAt NullTime `json:"last_seen_at,omitempty" format:"date-time"`
4243
Name string `json:"name"`
4344
Version string `json:"version"`
45+
APIVersion string `json:"api_version"`
4446
Provisioners []ProvisionerType `json:"provisioners"`
4547
Tags map[string]string `json:"tags"`
4648
}
@@ -212,6 +214,7 @@ func (c *Client) ServeProvisionerDaemon(ctx context.Context, req ServeProvisione
212214
}
213215
headers := http.Header{}
214216

217+
headers.Set(BuildVersionHeader, buildinfo.Version())
215218
if req.PreSharedKey == "" {
216219
// use session token if we don't have a PSK.
217220
jar, err := cookiejar.New(nil)

codersdk/users.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ func (c *Client) HasFirstUser(ctx context.Context) (bool, error) {
203203
if res.StatusCode == http.StatusNotFound {
204204
// ensure we are talking to coder and not
205205
// some other service that returns 404
206-
v := res.Header.Get("X-Coder-Build-Version")
206+
v := res.Header.Get(BuildVersionHeader)
207207
if v == "" {
208208
return false, xerrors.Errorf("missing build version header, not a coder instance")
209209
}

docs/api/enterprise.md

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/api/schemas.md

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

enterprise/cli/provisionerdaemons_test.go

+9
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"github.com/stretchr/testify/assert"
88
"github.com/stretchr/testify/require"
99

10+
"github.com/coder/coder/v2/buildinfo"
1011
"github.com/coder/coder/v2/cli/clitest"
1112
"github.com/coder/coder/v2/coderd/coderdtest"
1213
"github.com/coder/coder/v2/coderd/rbac"
@@ -49,6 +50,8 @@ func TestProvisionerDaemon_PSK(t *testing.T) {
4950
}, testutil.WaitShort, testutil.IntervalSlow)
5051
require.Equal(t, "matt-daemon", daemons[0].Name)
5152
require.Equal(t, provisionersdk.ScopeOrganization, daemons[0].Tags[provisionersdk.TagScope])
53+
require.Equal(t, buildinfo.Version(), daemons[0].Version)
54+
require.Equal(t, provisionersdk.APIVersionCurrent, daemons[0].APIVersion)
5255
}
5356

5457
func TestProvisionerDaemon_SessionToken(t *testing.T) {
@@ -84,6 +87,8 @@ func TestProvisionerDaemon_SessionToken(t *testing.T) {
8487
assert.Equal(t, "my-daemon", daemons[0].Name)
8588
assert.Equal(t, provisionersdk.ScopeUser, daemons[0].Tags[provisionersdk.TagScope])
8689
assert.Equal(t, anotherUser.ID.String(), daemons[0].Tags[provisionersdk.TagOwner])
90+
assert.Equal(t, buildinfo.Version(), daemons[0].Version)
91+
assert.Equal(t, provisionersdk.APIVersionCurrent, daemons[0].APIVersion)
8792
})
8893

8994
t.Run("ScopeAnotherUser", func(t *testing.T) {
@@ -118,6 +123,8 @@ func TestProvisionerDaemon_SessionToken(t *testing.T) {
118123
assert.Equal(t, provisionersdk.ScopeUser, daemons[0].Tags[provisionersdk.TagScope])
119124
// This should get clobbered to the user who started the daemon.
120125
assert.Equal(t, anotherUser.ID.String(), daemons[0].Tags[provisionersdk.TagOwner])
126+
assert.Equal(t, buildinfo.Version(), daemons[0].Version)
127+
assert.Equal(t, provisionersdk.APIVersionCurrent, daemons[0].APIVersion)
121128
})
122129

123130
t.Run("ScopeOrg", func(t *testing.T) {
@@ -150,5 +157,7 @@ func TestProvisionerDaemon_SessionToken(t *testing.T) {
150157
}, testutil.WaitShort, testutil.IntervalSlow)
151158
assert.Equal(t, "org-daemon", daemons[0].Name)
152159
assert.Equal(t, provisionersdk.ScopeOrganization, daemons[0].Tags[provisionersdk.TagScope])
160+
assert.Equal(t, buildinfo.Version(), daemons[0].Version)
161+
assert.Equal(t, provisionersdk.APIVersionCurrent, daemons[0].APIVersion)
153162
})
154163
}

enterprise/cli/server_dbcrypt_test.go

+5-12
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ import (
1515
"github.com/coder/coder/v2/coderd/database/dbgen"
1616
"github.com/coder/coder/v2/coderd/database/dbtestutil"
1717
"github.com/coder/coder/v2/coderd/database/postgres"
18-
"github.com/coder/coder/v2/cryptorand"
1918
"github.com/coder/coder/v2/enterprise/dbcrypt"
2019
"github.com/coder/coder/v2/pty/ptytest"
20+
"github.com/coder/coder/v2/testutil"
2121
)
2222

2323
// TestServerDBCrypt tests end-to-end encryption, decryption, and deletion
@@ -50,7 +50,7 @@ func TestServerDBCrypt(t *testing.T) {
5050
users := genData(t, db)
5151

5252
// Setup an initial cipher A
53-
keyA := mustString(t, 32)
53+
keyA := testutil.MustRandString(t, 32)
5454
cipherA, err := dbcrypt.NewCiphers([]byte(keyA))
5555
require.NoError(t, err)
5656

@@ -87,7 +87,7 @@ func TestServerDBCrypt(t *testing.T) {
8787
}
8888

8989
// Re-encrypt all existing data with a new cipher.
90-
keyB := mustString(t, 32)
90+
keyB := testutil.MustRandString(t, 32)
9191
cipherBA, err := dbcrypt.NewCiphers([]byte(keyB), []byte(keyA))
9292
require.NoError(t, err)
9393

@@ -160,7 +160,7 @@ func TestServerDBCrypt(t *testing.T) {
160160
}
161161

162162
// Re-encrypt all existing data with a new cipher.
163-
keyC := mustString(t, 32)
163+
keyC := testutil.MustRandString(t, 32)
164164
cipherC, err := dbcrypt.NewCiphers([]byte(keyC))
165165
require.NoError(t, err)
166166

@@ -222,7 +222,7 @@ func genData(t *testing.T, db database.Store) []database.User {
222222
for _, status := range database.AllUserStatusValues() {
223223
for _, loginType := range database.AllLoginTypeValues() {
224224
for _, deleted := range []bool{false, true} {
225-
randName := mustString(t, 32)
225+
randName := testutil.MustRandString(t, 32)
226226
usr := dbgen.User(t, db, database.User{
227227
Username: randName,
228228
Email: randName + "@notcoder.com",
@@ -252,13 +252,6 @@ func genData(t *testing.T, db database.Store) []database.User {
252252
return users
253253
}
254254

255-
func mustString(t *testing.T, n int) string {
256-
t.Helper()
257-
s, err := cryptorand.String(n)
258-
require.NoError(t, err)
259-
return s
260-
}
261-
262255
func requireEncryptedEquals(t *testing.T, c dbcrypt.Cipher, expected, actual string) {
263256
t.Helper()
264257
var decodedVal []byte

enterprise/coderd/provisionerdaemons.go

+5-2
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,8 @@ func (api *API) provisionerDaemonServe(rw http.ResponseWriter, r *http.Request)
233233
authCtx = dbauthz.AsSystemRestricted(ctx)
234234
}
235235

236+
versionHdrVal := r.Header.Get(codersdk.BuildVersionHeader)
237+
236238
// Create the daemon in the database.
237239
now := dbtime.Now()
238240
daemon, err := api.Database.UpsertProvisionerDaemon(authCtx, database.UpsertProvisionerDaemonParams{
@@ -241,8 +243,8 @@ func (api *API) provisionerDaemonServe(rw http.ResponseWriter, r *http.Request)
241243
Tags: tags,
242244
CreatedAt: now,
243245
LastSeenAt: sql.NullTime{Time: now, Valid: true},
244-
Version: "", // TODO: provisionerd needs to send version
245-
APIVersion: "1.0",
246+
Version: versionHdrVal,
247+
APIVersion: provisionersdk.APIVersionCurrent,
246248
})
247249
if err != nil {
248250
if !xerrors.Is(err, context.Canceled) {
@@ -361,6 +363,7 @@ func convertProvisionerDaemon(daemon database.ProvisionerDaemon) codersdk.Provis
361363
Name: daemon.Name,
362364
Tags: daemon.Tags,
363365
Version: daemon.Version,
366+
APIVersion: daemon.APIVersion,
364367
}
365368
for _, provisionerType := range daemon.Provisioners {
366369
result.Provisioners = append(result.Provisioners, codersdk.ProvisionerType(provisionerType))

0 commit comments

Comments
 (0)