Skip to content

Commit 691c328

Browse files
committed
Merge remote-tracking branch 'origin/main' into dean/move-app-proxy-routes
2 parents fe28e42 + 00d468b commit 691c328

File tree

119 files changed

+3758
-659
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

119 files changed

+3758
-659
lines changed

cli/cliui/output.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package cliui
33
import (
44
"context"
55
"encoding/json"
6+
"fmt"
67
"reflect"
78
"strings"
89

@@ -171,3 +172,23 @@ func (jsonFormat) Format(_ context.Context, data any) (string, error) {
171172

172173
return string(outBytes), nil
173174
}
175+
176+
type textFormat struct{}
177+
178+
var _ OutputFormat = textFormat{}
179+
180+
// TextFormat is a formatter that just outputs unstructured text.
181+
// It uses fmt.Sprintf under the hood.
182+
func TextFormat() OutputFormat {
183+
return textFormat{}
184+
}
185+
186+
func (textFormat) ID() string {
187+
return "text"
188+
}
189+
190+
func (textFormat) AttachOptions(_ *clibase.OptionSet) {}
191+
192+
func (textFormat) Format(_ context.Context, data any) (string, error) {
193+
return fmt.Sprintf("%s", data), nil
194+
}

cli/cliui/output_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ func Test_OutputFormatter(t *testing.T) {
5050
require.Panics(t, func() {
5151
cliui.NewOutputFormatter(cliui.JSONFormat())
5252
})
53+
require.NotPanics(t, func() {
54+
cliui.NewOutputFormatter(cliui.JSONFormat(), cliui.TextFormat())
55+
})
5356
})
5457

5558
t.Run("NoMissingFormatID", func(t *testing.T) {

cli/create.go

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"io"
77
"time"
88

9+
"github.com/google/uuid"
910
"golang.org/x/exp/slices"
1011
"golang.org/x/xerrors"
1112

@@ -213,6 +214,7 @@ type prepWorkspaceBuildArgs struct {
213214
NewWorkspaceName string
214215

215216
UpdateWorkspace bool
217+
WorkspaceID uuid.UUID
216218
}
217219

218220
type buildParameters struct {
@@ -340,8 +342,17 @@ PromptRichParamLoop:
340342
}
341343

342344
if args.UpdateWorkspace && !templateVersionParameter.Mutable {
343-
_, _ = fmt.Fprintln(inv.Stdout, cliui.Styles.Warn.Render(fmt.Sprintf(`Parameter %q is not mutable, so can't be customized after workspace creation.`, templateVersionParameter.Name)))
344-
continue
345+
// Check if the immutable parameter was used in the previous build. If so, then it isn't a fresh one
346+
// and the user should be warned.
347+
exists, err := workspaceBuildParameterExists(ctx, client, args.WorkspaceID, templateVersionParameter)
348+
if err != nil {
349+
return nil, err
350+
}
351+
352+
if exists {
353+
_, _ = fmt.Fprintln(inv.Stdout, cliui.Styles.Warn.Render(fmt.Sprintf(`Parameter %q is not mutable, so can't be customized after workspace creation.`, templateVersionParameter.Name)))
354+
continue
355+
}
345356
}
346357

347358
parameterValue, err := getWorkspaceBuildParameterValueFromMapOrInput(inv, parameterMapFromFile, templateVersionParameter)
@@ -414,3 +425,17 @@ PromptRichParamLoop:
414425
richParameters: richParameters,
415426
}, nil
416427
}
428+
429+
func workspaceBuildParameterExists(ctx context.Context, client *codersdk.Client, workspaceID uuid.UUID, templateVersionParameter codersdk.TemplateVersionParameter) (bool, error) {
430+
lastBuildParameters, err := client.WorkspaceBuildParameters(ctx, workspaceID)
431+
if err != nil {
432+
return false, xerrors.Errorf("can't fetch last workspace build parameters: %w", err)
433+
}
434+
435+
for _, p := range lastBuildParameters {
436+
if p.Name == templateVersionParameter.Name {
437+
return true, nil
438+
}
439+
}
440+
return false, nil
441+
}

cli/root.go

Lines changed: 1 addition & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ func (r *RootCmd) Core() []*clibase.Cmd {
8282
r.templates(),
8383
r.users(),
8484
r.tokens(),
85-
r.version(),
85+
r.version(defaultVersionInfo),
8686

8787
// Workspace Commands
8888
r.configSSH(),
@@ -370,36 +370,6 @@ func LoggerFromContext(ctx context.Context) (slog.Logger, bool) {
370370
return l, ok
371371
}
372372

373-
// version prints the coder version
374-
func (*RootCmd) version() *clibase.Cmd {
375-
return &clibase.Cmd{
376-
Use: "version",
377-
Short: "Show coder version",
378-
Handler: func(inv *clibase.Invocation) error {
379-
var str strings.Builder
380-
_, _ = str.WriteString("Coder ")
381-
if buildinfo.IsAGPL() {
382-
_, _ = str.WriteString("(AGPL) ")
383-
}
384-
_, _ = str.WriteString(buildinfo.Version())
385-
buildTime, valid := buildinfo.Time()
386-
if valid {
387-
_, _ = str.WriteString(" " + buildTime.Format(time.UnixDate))
388-
}
389-
_, _ = str.WriteString("\r\n" + buildinfo.ExternalURL() + "\r\n\r\n")
390-
391-
if buildinfo.IsSlim() {
392-
_, _ = str.WriteString(fmt.Sprintf("Slim build of Coder, does not support the %s subcommand.\n", cliui.Styles.Code.Render("server")))
393-
} else {
394-
_, _ = str.WriteString(fmt.Sprintf("Full build of Coder, supports the %s subcommand.\n", cliui.Styles.Code.Render("server")))
395-
}
396-
397-
_, _ = fmt.Fprint(inv.Stdout, str.String())
398-
return nil
399-
},
400-
}
401-
}
402-
403373
func isTest() bool {
404374
return flag.Lookup("test.v") != nil
405375
}

cli/server.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
"strconv"
3131
"strings"
3232
"sync"
33+
"sync/atomic"
3334
"time"
3435

3536
"github.com/coreos/go-oidc/v3/oidc"
@@ -72,6 +73,7 @@ import (
7273
"github.com/coder/coder/coderd/httpapi"
7374
"github.com/coder/coder/coderd/httpmw"
7475
"github.com/coder/coder/coderd/prometheusmetrics"
76+
"github.com/coder/coder/coderd/schedule"
7577
"github.com/coder/coder/coderd/telemetry"
7678
"github.com/coder/coder/coderd/tracing"
7779
"github.com/coder/coder/coderd/updatecheck"
@@ -633,6 +635,7 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
633635
LoginRateLimit: loginRateLimit,
634636
FilesRateLimit: filesRateLimit,
635637
HTTPClient: httpClient,
638+
TemplateScheduleStore: &atomic.Pointer[schedule.TemplateScheduleStore]{},
636639
SSHConfig: codersdk.SSHConfigResponse{
637640
HostnamePrefix: cfg.SSHConfig.DeploymentName.String(),
638641
SSHConfigOptions: configSSHOptions,
@@ -729,6 +732,7 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
729732
UsernameField: cfg.OIDC.UsernameField.String(),
730733
EmailField: cfg.OIDC.EmailField.String(),
731734
AuthURLParams: cfg.OIDC.AuthURLParams.Value,
735+
IgnoreUserInfo: cfg.OIDC.IgnoreUserInfo.Value(),
732736
GroupField: cfg.OIDC.GroupField.String(),
733737
GroupMapping: cfg.OIDC.GroupMapping.Value,
734738
SignInText: cfg.OIDC.SignInText.String(),
@@ -1025,7 +1029,7 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
10251029

10261030
autobuildPoller := time.NewTicker(cfg.AutobuildPollInterval.Value())
10271031
defer autobuildPoller.Stop()
1028-
autobuildExecutor := executor.New(ctx, options.Database, logger, autobuildPoller.C)
1032+
autobuildExecutor := executor.New(ctx, options.Database, coderAPI.TemplateScheduleStore, logger, autobuildPoller.C)
10291033
autobuildExecutor.Run()
10301034

10311035
// Currently there is no way to ask the server to shut

cli/server_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1089,6 +1089,7 @@ func TestServer(t *testing.T) {
10891089
require.Equal(t, "preferred_username", deploymentConfig.Values.OIDC.UsernameField.Value())
10901090
require.Equal(t, "email", deploymentConfig.Values.OIDC.EmailField.Value())
10911091
require.Equal(t, map[string]string{"access_type": "offline"}, deploymentConfig.Values.OIDC.AuthURLParams.Value)
1092+
require.False(t, deploymentConfig.Values.OIDC.IgnoreUserInfo.Value())
10921093
require.Empty(t, deploymentConfig.Values.OIDC.GroupField.Value())
10931094
require.Empty(t, deploymentConfig.Values.OIDC.GroupMapping.Value)
10941095
require.Equal(t, "OpenID Connect", deploymentConfig.Values.OIDC.SignInText.Value())
@@ -1128,6 +1129,7 @@ func TestServer(t *testing.T) {
11281129
"--oidc-username-field", "not_preferred_username",
11291130
"--oidc-email-field", "not_email",
11301131
"--oidc-auth-url-params", `{"prompt":"consent"}`,
1132+
"--oidc-ignore-userinfo",
11311133
"--oidc-group-field", "serious_business_unit",
11321134
"--oidc-group-mapping", `{"serious_business_unit": "serious_business_unit"}`,
11331135
"--oidc-sign-in-text", "Sign In With Coder",
@@ -1172,6 +1174,7 @@ func TestServer(t *testing.T) {
11721174
require.True(t, deploymentConfig.Values.OIDC.IgnoreEmailVerified.Value())
11731175
require.Equal(t, "not_preferred_username", deploymentConfig.Values.OIDC.UsernameField.Value())
11741176
require.Equal(t, "not_email", deploymentConfig.Values.OIDC.EmailField.Value())
1177+
require.True(t, deploymentConfig.Values.OIDC.IgnoreUserInfo.Value())
11751178
require.Equal(t, map[string]string{"prompt": "consent"}, deploymentConfig.Values.OIDC.AuthURLParams.Value)
11761179
require.Equal(t, "serious_business_unit", deploymentConfig.Values.OIDC.GroupField.Value())
11771180
require.Equal(t, map[string]string{"serious_business_unit": "serious_business_unit"}, deploymentConfig.Values.OIDC.GroupMapping.Value)

cli/templateedit.go

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ func (r *RootCmd) templateEdit() *clibase.Cmd {
2121
defaultTTL time.Duration
2222
maxTTL time.Duration
2323
allowUserCancelWorkspaceJobs bool
24+
allowUserAutostart bool
25+
allowUserAutostop bool
2426
)
2527
client := new(codersdk.Client)
2628

@@ -32,17 +34,17 @@ func (r *RootCmd) templateEdit() *clibase.Cmd {
3234
),
3335
Short: "Edit the metadata of a template by name.",
3436
Handler: func(inv *clibase.Invocation) error {
35-
if maxTTL != 0 {
37+
if maxTTL != 0 || !allowUserAutostart || !allowUserAutostop {
3638
entitlements, err := client.Entitlements(inv.Context())
3739
var sdkErr *codersdk.Error
3840
if xerrors.As(err, &sdkErr) && sdkErr.StatusCode() == http.StatusNotFound {
39-
return xerrors.Errorf("your deployment appears to be an AGPL deployment, so you cannot set --max-ttl")
41+
return xerrors.Errorf("your deployment appears to be an AGPL deployment, so you cannot set --max-ttl, --allow-user-autostart=false or --allow-user-autostop=false")
4042
} else if err != nil {
4143
return xerrors.Errorf("get entitlements: %w", err)
4244
}
4345

4446
if !entitlements.Features[codersdk.FeatureAdvancedTemplateScheduling].Enabled {
45-
return xerrors.Errorf("your license is not entitled to use advanced template scheduling, so you cannot set --max-ttl")
47+
return xerrors.Errorf("your license is not entitled to use advanced template scheduling, so you cannot set --max-ttl, --allow-user-autostart=false or --allow-user-autostop=false")
4648
}
4749
}
4850

@@ -64,6 +66,8 @@ func (r *RootCmd) templateEdit() *clibase.Cmd {
6466
DefaultTTLMillis: defaultTTL.Milliseconds(),
6567
MaxTTLMillis: maxTTL.Milliseconds(),
6668
AllowUserCancelWorkspaceJobs: allowUserCancelWorkspaceJobs,
69+
AllowUserAutostart: allowUserAutostart,
70+
AllowUserAutostop: allowUserAutostop,
6771
}
6872

6973
_, err = client.UpdateTemplateMeta(inv.Context(), template.ID, req)
@@ -112,6 +116,18 @@ func (r *RootCmd) templateEdit() *clibase.Cmd {
112116
Default: "true",
113117
Value: clibase.BoolOf(&allowUserCancelWorkspaceJobs),
114118
},
119+
{
120+
Flag: "allow-user-autostart",
121+
Description: "Allow users to configure autostart for workspaces on this template. This can only be disabled in enterprise.",
122+
Default: "true",
123+
Value: clibase.BoolOf(&allowUserAutostart),
124+
},
125+
{
126+
Flag: "allow-user-autostop",
127+
Description: "Allow users to customize the autostop TTL for workspaces on this template. This can only be disabled in enterprise.",
128+
Default: "true",
129+
Value: clibase.BoolOf(&allowUserAutostop),
130+
},
115131
cliui.SkipPromptOption(),
116132
}
117133

0 commit comments

Comments
 (0)