Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit ca606a4

Browse files
committed
Merge branch 'main' of github.com:coder/coder into bq/fe-examples
2 parents 5b01321 + 560c8ce commit ca606a4

24 files changed

+966
-808
lines changed

README.md

Lines changed: 18 additions & 11 deletions
Large diffs are not rendered by default.

agent/agent_test.go

Lines changed: 571 additions & 578 deletions
Large diffs are not rendered by default.

agent/apphealth_test.go

Lines changed: 127 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -19,148 +19,145 @@ import (
1919
"github.com/coder/coder/testutil"
2020
)
2121

22-
func TestAppHealth(t *testing.T) {
22+
func TestAppHealth_Healthy(t *testing.T) {
2323
t.Parallel()
24-
t.Run("Healthy", func(t *testing.T) {
25-
t.Parallel()
26-
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
27-
defer cancel()
28-
apps := []codersdk.WorkspaceApp{
29-
{
30-
Slug: "app1",
31-
Healthcheck: codersdk.Healthcheck{},
32-
Health: codersdk.WorkspaceAppHealthDisabled,
24+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
25+
defer cancel()
26+
apps := []codersdk.WorkspaceApp{
27+
{
28+
Slug: "app1",
29+
Healthcheck: codersdk.Healthcheck{},
30+
Health: codersdk.WorkspaceAppHealthDisabled,
31+
},
32+
{
33+
Slug: "app2",
34+
Healthcheck: codersdk.Healthcheck{
35+
// URL: We don't set the URL for this test because the setup will
36+
// create a httptest server for us and set it for us.
37+
Interval: 1,
38+
Threshold: 1,
3339
},
34-
{
35-
Slug: "app2",
36-
Healthcheck: codersdk.Healthcheck{
37-
// URL: We don't set the URL for this test because the setup will
38-
// create a httptest server for us and set it for us.
39-
Interval: 1,
40-
Threshold: 1,
41-
},
42-
Health: codersdk.WorkspaceAppHealthInitializing,
43-
},
44-
}
45-
handlers := []http.Handler{
46-
nil,
47-
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
48-
httpapi.Write(r.Context(), w, http.StatusOK, nil)
49-
}),
50-
}
51-
getApps, closeFn := setupAppReporter(ctx, t, apps, handlers)
52-
defer closeFn()
40+
Health: codersdk.WorkspaceAppHealthInitializing,
41+
},
42+
}
43+
handlers := []http.Handler{
44+
nil,
45+
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
46+
httpapi.Write(r.Context(), w, http.StatusOK, nil)
47+
}),
48+
}
49+
getApps, closeFn := setupAppReporter(ctx, t, apps, handlers)
50+
defer closeFn()
51+
apps, err := getApps(ctx)
52+
require.NoError(t, err)
53+
require.EqualValues(t, codersdk.WorkspaceAppHealthDisabled, apps[0].Health)
54+
require.Eventually(t, func() bool {
5355
apps, err := getApps(ctx)
54-
require.NoError(t, err)
55-
require.EqualValues(t, codersdk.WorkspaceAppHealthDisabled, apps[0].Health)
56-
require.Eventually(t, func() bool {
57-
apps, err := getApps(ctx)
58-
if err != nil {
59-
return false
60-
}
56+
if err != nil {
57+
return false
58+
}
6159

62-
return apps[1].Health == codersdk.WorkspaceAppHealthHealthy
63-
}, testutil.WaitLong, testutil.IntervalSlow)
64-
})
65-
66-
t.Run("500", func(t *testing.T) {
67-
t.Parallel()
68-
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
69-
defer cancel()
70-
apps := []codersdk.WorkspaceApp{
71-
{
72-
Slug: "app2",
73-
Healthcheck: codersdk.Healthcheck{
74-
// URL: We don't set the URL for this test because the setup will
75-
// create a httptest server for us and set it for us.
76-
Interval: 1,
77-
Threshold: 1,
78-
},
79-
Health: codersdk.WorkspaceAppHealthInitializing,
60+
return apps[1].Health == codersdk.WorkspaceAppHealthHealthy
61+
}, testutil.WaitLong, testutil.IntervalSlow)
62+
}
63+
64+
func TestAppHealth_500(t *testing.T) {
65+
t.Parallel()
66+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
67+
defer cancel()
68+
apps := []codersdk.WorkspaceApp{
69+
{
70+
Slug: "app2",
71+
Healthcheck: codersdk.Healthcheck{
72+
// URL: We don't set the URL for this test because the setup will
73+
// create a httptest server for us and set it for us.
74+
Interval: 1,
75+
Threshold: 1,
8076
},
77+
Health: codersdk.WorkspaceAppHealthInitializing,
78+
},
79+
}
80+
handlers := []http.Handler{
81+
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
82+
httpapi.Write(r.Context(), w, http.StatusInternalServerError, nil)
83+
}),
84+
}
85+
getApps, closeFn := setupAppReporter(ctx, t, apps, handlers)
86+
defer closeFn()
87+
require.Eventually(t, func() bool {
88+
apps, err := getApps(ctx)
89+
if err != nil {
90+
return false
8191
}
82-
handlers := []http.Handler{
83-
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
84-
httpapi.Write(r.Context(), w, http.StatusInternalServerError, nil)
85-
}),
86-
}
87-
getApps, closeFn := setupAppReporter(ctx, t, apps, handlers)
88-
defer closeFn()
89-
require.Eventually(t, func() bool {
90-
apps, err := getApps(ctx)
91-
if err != nil {
92-
return false
93-
}
9492

95-
return apps[0].Health == codersdk.WorkspaceAppHealthUnhealthy
96-
}, testutil.WaitLong, testutil.IntervalSlow)
97-
})
98-
99-
t.Run("Timeout", func(t *testing.T) {
100-
t.Parallel()
101-
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
102-
defer cancel()
103-
apps := []codersdk.WorkspaceApp{
104-
{
105-
Slug: "app2",
106-
Healthcheck: codersdk.Healthcheck{
107-
// URL: We don't set the URL for this test because the setup will
108-
// create a httptest server for us and set it for us.
109-
Interval: 1,
110-
Threshold: 1,
111-
},
112-
Health: codersdk.WorkspaceAppHealthInitializing,
93+
return apps[0].Health == codersdk.WorkspaceAppHealthUnhealthy
94+
}, testutil.WaitLong, testutil.IntervalSlow)
95+
}
96+
97+
func TestAppHealth_Timeout(t *testing.T) {
98+
t.Parallel()
99+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
100+
defer cancel()
101+
apps := []codersdk.WorkspaceApp{
102+
{
103+
Slug: "app2",
104+
Healthcheck: codersdk.Healthcheck{
105+
// URL: We don't set the URL for this test because the setup will
106+
// create a httptest server for us and set it for us.
107+
Interval: 1,
108+
Threshold: 1,
113109
},
110+
Health: codersdk.WorkspaceAppHealthInitializing,
111+
},
112+
}
113+
handlers := []http.Handler{
114+
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
115+
// sleep longer than the interval to cause the health check to time out
116+
time.Sleep(2 * time.Second)
117+
httpapi.Write(r.Context(), w, http.StatusOK, nil)
118+
}),
119+
}
120+
getApps, closeFn := setupAppReporter(ctx, t, apps, handlers)
121+
defer closeFn()
122+
require.Eventually(t, func() bool {
123+
apps, err := getApps(ctx)
124+
if err != nil {
125+
return false
114126
}
115-
handlers := []http.Handler{
116-
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
117-
// sleep longer than the interval to cause the health check to time out
118-
time.Sleep(2 * time.Second)
119-
httpapi.Write(r.Context(), w, http.StatusOK, nil)
120-
}),
121-
}
122-
getApps, closeFn := setupAppReporter(ctx, t, apps, handlers)
123-
defer closeFn()
124-
require.Eventually(t, func() bool {
125-
apps, err := getApps(ctx)
126-
if err != nil {
127-
return false
128-
}
129127

130-
return apps[0].Health == codersdk.WorkspaceAppHealthUnhealthy
131-
}, testutil.WaitLong, testutil.IntervalSlow)
132-
})
133-
134-
t.Run("NotSpamming", func(t *testing.T) {
135-
t.Parallel()
136-
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
137-
defer cancel()
138-
apps := []codersdk.WorkspaceApp{
139-
{
140-
Slug: "app2",
141-
Healthcheck: codersdk.Healthcheck{
142-
// URL: We don't set the URL for this test because the setup will
143-
// create a httptest server for us and set it for us.
144-
Interval: 1,
145-
Threshold: 1,
146-
},
147-
Health: codersdk.WorkspaceAppHealthInitializing,
128+
return apps[0].Health == codersdk.WorkspaceAppHealthUnhealthy
129+
}, testutil.WaitLong, testutil.IntervalSlow)
130+
}
131+
132+
func TestAppHealth_NotSpamming(t *testing.T) {
133+
t.Parallel()
134+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
135+
defer cancel()
136+
apps := []codersdk.WorkspaceApp{
137+
{
138+
Slug: "app2",
139+
Healthcheck: codersdk.Healthcheck{
140+
// URL: We don't set the URL for this test because the setup will
141+
// create a httptest server for us and set it for us.
142+
Interval: 1,
143+
Threshold: 1,
148144
},
149-
}
145+
Health: codersdk.WorkspaceAppHealthInitializing,
146+
},
147+
}
150148

151-
var counter = new(int32)
152-
handlers := []http.Handler{
153-
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
154-
atomic.AddInt32(counter, 1)
155-
}),
156-
}
157-
_, closeFn := setupAppReporter(ctx, t, apps, handlers)
158-
defer closeFn()
159-
// Ensure we haven't made more than 2 (expected 1 + 1 for buffer) requests in the last second.
160-
// if there is a bug where we are spamming the healthcheck route this will catch it.
161-
time.Sleep(time.Second)
162-
require.LessOrEqual(t, *counter, int32(2))
163-
})
149+
counter := new(int32)
150+
handlers := []http.Handler{
151+
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
152+
atomic.AddInt32(counter, 1)
153+
}),
154+
}
155+
_, closeFn := setupAppReporter(ctx, t, apps, handlers)
156+
defer closeFn()
157+
// Ensure we haven't made more than 2 (expected 1 + 1 for buffer) requests in the last second.
158+
// if there is a bug where we are spamming the healthcheck route this will catch it.
159+
time.Sleep(time.Second)
160+
require.LessOrEqual(t, *counter, int32(2))
164161
}
165162

166163
func setupAppReporter(ctx context.Context, t *testing.T, apps []codersdk.WorkspaceApp, handlers []http.Handler) (agent.WorkspaceAgentApps, func()) {

cli/deployment/config.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,12 @@ func newConfig() *codersdk.DeploymentConfig {
424424
Flag: "update-check",
425425
Default: flag.Lookup("test.v") == nil && !buildinfo.IsDev(),
426426
},
427+
MaxTokenLifetime: &codersdk.DeploymentConfigField[time.Duration]{
428+
Name: "Max Token Lifetime",
429+
Usage: "The maximum lifetime duration for any user creating a token.",
430+
Flag: "max-token-lifetime",
431+
Default: 24 * 30 * time.Hour,
432+
},
427433
}
428434
}
429435

cli/testdata/coder_server_--help.golden

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ Flags:
6565
production.
6666
Consumes $CODER_EXPERIMENTAL
6767
-h, --help help for server
68+
--max-token-lifetime duration The maximum lifetime duration for any
69+
user creating a token.
70+
Consumes $CODER_MAX_TOKEN_LIFETIME
71+
(default 720h0m0s)
6872
--oauth2-github-allow-everyone Allow all logins, setting this option
6973
means allowed orgs and teams must be
7074
empty.

cli/tokens.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"github.com/spf13/cobra"
99
"golang.org/x/xerrors"
1010

11+
"github.com/coder/coder/cli/cliflag"
1112
"github.com/coder/coder/cli/cliui"
1213
"github.com/coder/coder/codersdk"
1314
)
@@ -46,6 +47,9 @@ func tokens() *cobra.Command {
4647
}
4748

4849
func createToken() *cobra.Command {
50+
var (
51+
tokenLifetime time.Duration
52+
)
4953
cmd := &cobra.Command{
5054
Use: "create",
5155
Short: "Create a tokens",
@@ -55,7 +59,9 @@ func createToken() *cobra.Command {
5559
return xerrors.Errorf("create codersdk client: %w", err)
5660
}
5761

58-
res, err := client.CreateToken(cmd.Context(), codersdk.Me, codersdk.CreateTokenRequest{})
62+
res, err := client.CreateToken(cmd.Context(), codersdk.Me, codersdk.CreateTokenRequest{
63+
Lifetime: tokenLifetime,
64+
})
5965
if err != nil {
6066
return xerrors.Errorf("create tokens: %w", err)
6167
}
@@ -74,6 +80,8 @@ func createToken() *cobra.Command {
7480
},
7581
}
7682

83+
cliflag.DurationVarP(cmd.Flags(), &tokenLifetime, "lifetime", "", "CODER_TOKEN_LIFETIME", 30*24*time.Hour, "Specify a duration for the lifetime of the token.")
84+
7785
return cmd
7886
}
7987

coderd/apikey.go

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,21 @@ func (api *API) postToken(rw http.ResponseWriter, r *http.Request) {
4444
scope = database.APIKeyScope(createToken.Scope)
4545
}
4646

47-
// tokens last 100 years
48-
lifeTime := time.Hour * 876000
47+
// default lifetime is 30 days
48+
lifeTime := 30 * 24 * time.Hour
49+
if createToken.Lifetime != 0 {
50+
lifeTime = createToken.Lifetime
51+
}
52+
53+
err := api.validateAPIKeyLifetime(lifeTime)
54+
if err != nil {
55+
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
56+
Message: "Failed to validate create API key request.",
57+
Detail: err.Error(),
58+
})
59+
return
60+
}
61+
4962
cookie, err := api.createAPIKey(ctx, createAPIKeyParams{
5063
UserID: user.ID,
5164
LoginType: database.LoginTypeToken,
@@ -65,7 +78,6 @@ func (api *API) postToken(rw http.ResponseWriter, r *http.Request) {
6578
}
6679

6780
// Creates a new session key, used for logging in via the CLI.
68-
// DEPRECATED: use postToken instead.
6981
func (api *API) postAPIKey(rw http.ResponseWriter, r *http.Request) {
7082
ctx := r.Context()
7183
user := httpmw.UserParam(r)
@@ -214,6 +226,18 @@ type createAPIKeyParams struct {
214226
Scope database.APIKeyScope
215227
}
216228

229+
func (api *API) validateAPIKeyLifetime(lifetime time.Duration) error {
230+
if lifetime <= 0 {
231+
return xerrors.New("lifetime must be positive number greater than 0")
232+
}
233+
234+
if lifetime > api.DeploymentConfig.MaxTokenLifetime.Value {
235+
return xerrors.Errorf("lifetime must be less than %s", api.DeploymentConfig.MaxTokenLifetime.Value)
236+
}
237+
238+
return nil
239+
}
240+
217241
func (api *API) createAPIKey(ctx context.Context, params createAPIKeyParams) (*http.Cookie, error) {
218242
keyID, keySecret, err := generateAPIKeyIDSecret()
219243
if err != nil {

0 commit comments

Comments
 (0)