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

Skip to content

Commit c66852f

Browse files
committed
feat: deprecate the oauth2 experiment in favor of CODER_OAUTH2_PROVIDER_ENABLE
The oauth2 experiment no longer does anything. ReadExperiments drops it from the result and logs one warning per process naming the flag, so an admin whose OAuth2 clients start getting 404s after upgrading sees why. The constant stays known for one release so the warning can be specific; the next release removes it.
1 parent cf2e665 commit c66852f

6 files changed

Lines changed: 117 additions & 7 deletions

File tree

coderd/apidoc/docs.go

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/apidoc/swagger.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/coderd.go

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2578,15 +2578,39 @@ func (api *API) DERPMap() *tailcfg.DERPMap {
25782578
return api.BaseDERPMap
25792579
}
25802580

2581+
// oauth2ExperimentDeprecatedMessage is logged when the retired oauth2
2582+
// experiment is still configured. The OAuth2 provider is controlled by
2583+
// CODER_OAUTH2_PROVIDER_ENABLE, so the experiment value does nothing.
2584+
const oauth2ExperimentDeprecatedMessage = `CODER_EXPERIMENTS contains "oauth2", which is deprecated and has no effect. The OAuth2 provider is now generally available and disabled by default. Set CODER_OAUTH2_PROVIDER_ENABLE=true to enable it. The "oauth2" experiment value will be removed in the next release.`
2585+
2586+
// warnOAuth2ExperimentDeprecated limits the deprecation warning to once per
2587+
// process. coder server reads the experiment list several times during
2588+
// startup, and every read would otherwise repeat the line.
2589+
var warnOAuth2ExperimentDeprecated sync.Once
2590+
25812591
// nolint:revive
25822592
func ReadExperiments(log slog.Logger, raw []string) codersdk.Experiments {
2593+
return parseExperiments(log, raw, &warnOAuth2ExperimentDeprecated)
2594+
}
2595+
2596+
// parseExperiments takes the warning guard as a parameter so tests can check
2597+
// the once-only behavior with their own sync.Once instead of resetting the
2598+
// package-level one.
2599+
func parseExperiments(log slog.Logger, raw []string, warnOAuth2Once *sync.Once) codersdk.Experiments {
25832600
exps := make([]codersdk.Experiment, 0, len(raw))
25842601
for _, v := range raw {
2585-
switch v {
2602+
ex := codersdk.Experiment(strings.ToLower(v))
2603+
switch ex {
25862604
case "*":
25872605
exps = append(exps, codersdk.ExperimentsSafe...)
2606+
case codersdk.ExperimentOAuth2:
2607+
// Recognized but inert for one release so the warning can be
2608+
// specific. Deliberately not appended: nothing may observe the
2609+
// experiment as enabled.
2610+
warnOAuth2Once.Do(func() {
2611+
log.Warn(context.Background(), oauth2ExperimentDeprecatedMessage)
2612+
})
25882613
default:
2589-
ex := codersdk.Experiment(strings.ToLower(v))
25902614
if !slice.Contains(codersdk.ExperimentsKnown, ex) {
25912615
log.Warn(context.Background(), "ignoring unknown experiment", slog.F("experiment", ex))
25922616
} else if !slice.Contains(codersdk.ExperimentsSafe, ex) {
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package coderd
2+
3+
import (
4+
"context"
5+
"sync"
6+
"testing"
7+
8+
"github.com/stretchr/testify/require"
9+
10+
"cdr.dev/slog/v3"
11+
12+
"github.com/coder/coder/v2/codersdk"
13+
)
14+
15+
// logRecorder keeps every entry so a test can count log lines.
16+
type logRecorder struct {
17+
mu sync.Mutex
18+
entries []slog.SinkEntry
19+
}
20+
21+
func (s *logRecorder) LogEntry(_ context.Context, e slog.SinkEntry) {
22+
s.mu.Lock()
23+
defer s.mu.Unlock()
24+
s.entries = append(s.entries, e)
25+
}
26+
27+
func (*logRecorder) Sync() {}
28+
29+
func (s *logRecorder) messages(level slog.Level) []string {
30+
s.mu.Lock()
31+
defer s.mu.Unlock()
32+
var out []string
33+
for _, e := range s.entries {
34+
if e.Level == level {
35+
out = append(out, e.Message)
36+
}
37+
}
38+
return out
39+
}
40+
41+
func TestReadExperimentsDeprecatedOAuth2(t *testing.T) {
42+
t.Parallel()
43+
44+
rec := &logRecorder{}
45+
log := slog.Make(rec)
46+
var once sync.Once
47+
raw := []string{string(codersdk.ExperimentOAuth2), string(codersdk.ExperimentMCPServerHTTP)}
48+
49+
got := parseExperiments(log, raw, &once)
50+
require.Equal(t, codersdk.Experiments{codersdk.ExperimentMCPServerHTTP}, got,
51+
"the oauth2 experiment must be dropped, not passed through")
52+
require.Equal(t, []string{oauth2ExperimentDeprecatedMessage, "🐉 HERE BE DRAGONS: opting into hidden experiment"},
53+
rec.messages(slog.LevelWarn))
54+
55+
// A second read in the same process returns the same slice and does not
56+
// repeat the deprecation warning. Upper-case input is matched too.
57+
got = parseExperiments(log, []string{"OAuth2", string(codersdk.ExperimentMCPServerHTTP)}, &once)
58+
require.Equal(t, codersdk.Experiments{codersdk.ExperimentMCPServerHTTP}, got)
59+
var deprecations int
60+
for _, m := range rec.messages(slog.LevelWarn) {
61+
if m == oauth2ExperimentDeprecatedMessage {
62+
deprecations++
63+
}
64+
}
65+
require.Equal(t, 1, deprecations, "deprecation warning must be logged once per process")
66+
}

coderd/experiments_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,26 @@ func Test_Experiments(t *testing.T) {
9999
require.False(t, experiments.Enabled("herebedragons"))
100100
})
101101

102+
t.Run("deprecated oauth2 experiment is dropped", func(t *testing.T) {
103+
t.Parallel()
104+
cfg := coderdtest.DeploymentValues(t)
105+
cfg.Experiments = []string{string(codersdk.ExperimentOAuth2), string(codersdk.ExperimentMCPServerHTTP)}
106+
client := coderdtest.New(t, &coderdtest.Options{
107+
DeploymentValues: cfg,
108+
})
109+
_ = coderdtest.CreateFirstUser(t, client)
110+
111+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
112+
defer cancel()
113+
114+
experiments, err := client.Experiments(ctx)
115+
require.NoError(t, err)
116+
// The provider is controlled by CODER_OAUTH2_PROVIDER_ENABLE, so the
117+
// experiment must never be reported as enabled.
118+
require.ElementsMatch(t, []codersdk.Experiment{codersdk.ExperimentMCPServerHTTP}, experiments)
119+
require.False(t, experiments.Enabled(codersdk.ExperimentOAuth2))
120+
})
121+
102122
t.Run("Unauthorized", func(t *testing.T) {
103123
t.Parallel()
104124
cfg := coderdtest.DeploymentValues(t)

codersdk/deployment.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5192,7 +5192,7 @@ const (
51925192
ExperimentAutoFillParameters Experiment = "auto-fill-parameters" // This should not be taken out of experiments until we have redesigned the feature.
51935193
ExperimentNotifications Experiment = "notifications" // Sends notifications via SMTP and webhooks following certain events.
51945194
ExperimentWorkspaceUsage Experiment = "workspace-usage" // Enables the new workspace usage tracking.
5195-
ExperimentOAuth2 Experiment = "oauth2" // Enables OAuth2 provider functionality.
5195+
ExperimentOAuth2 Experiment = "oauth2" // Deprecated: has no effect; use CODER_OAUTH2_PROVIDER_ENABLE.
51965196
ExperimentMCPServerHTTP Experiment = "mcp-server-http" // Enables the MCP HTTP server functionality.
51975197
ExperimentMCPToolSearch Experiment = "mcp-tool-search" // Defers MCP tool schemas behind a searchable catalog in agent chats.
51985198
ExperimentWorkspaceBuildUpdates Experiment = "workspace-build-updates" // Enables publishing workspace build updates to the all builds pubsub channel.

0 commit comments

Comments
 (0)