Skip to content

Commit d37ea0f

Browse files
committed
feat: system-generated notifications
1 parent c1a3010 commit d37ea0f

35 files changed

+3367
-11
lines changed

.golangci.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,11 @@ linters-settings:
195195
- name: var-naming
196196
- name: waitgroup-by-value
197197

198+
# irrelevant as of Go v1.22: https://go.dev/blog/loopvar-preview
199+
govet:
200+
disable:
201+
- loopclosure
202+
198203
issues:
199204
# Rules listed here: https://github.com/securego/gosec#available-rules
200205
exclude-rules:

cli/server.go

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ import (
5353
"gopkg.in/yaml.v3"
5454
"tailscale.com/tailcfg"
5555

56+
"github.com/coder/coder/v2/coderd/database/dbauthz"
57+
5658
"cdr.dev/slog"
5759
"cdr.dev/slog/sloggers/sloghuman"
5860
"github.com/coder/coder/v2/buildinfo"
@@ -73,6 +75,7 @@ import (
7375
"github.com/coder/coder/v2/coderd/externalauth"
7476
"github.com/coder/coder/v2/coderd/gitsshkey"
7577
"github.com/coder/coder/v2/coderd/httpmw"
78+
"github.com/coder/coder/v2/coderd/notifications"
7679
"github.com/coder/coder/v2/coderd/oauthpki"
7780
"github.com/coder/coder/v2/coderd/prometheusmetrics"
7881
"github.com/coder/coder/v2/coderd/prometheusmetrics/insights"
@@ -660,6 +663,10 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
660663
options.OIDCConfig = oc
661664
}
662665

666+
experiments := coderd.ReadExperiments(
667+
options.Logger, options.DeploymentValues.Experiments.Value(),
668+
)
669+
663670
// We'll read from this channel in the select below that tracks shutdown. If it remains
664671
// nil, that case of the select will just never fire, but it's important not to have a
665672
// "bare" read on this channel.
@@ -969,6 +976,23 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
969976
options.WorkspaceUsageTracker = tracker
970977
defer tracker.Close()
971978

979+
// Manage notifications.
980+
var notificationsManager *notifications.Manager
981+
if experiments.Enabled(codersdk.ExperimentNotifications) {
982+
cfg := options.DeploymentValues.Notifications
983+
nlog := logger.Named("notifications-manager")
984+
notificationsManager, err = notifications.NewManager(cfg, options.Database, nlog, templateHelpers(options))
985+
if err != nil {
986+
return xerrors.Errorf("failed to instantiate notification manager: %w", err)
987+
}
988+
989+
// nolint:gocritic // TODO: create own role.
990+
notificationsManager.Run(dbauthz.AsSystemRestricted(ctx), int(cfg.WorkerCount.Value()))
991+
notifications.RegisterInstance(notificationsManager)
992+
} else {
993+
notifications.RegisterInstance(notifications.NewNoopManager())
994+
}
995+
972996
// Wrap the server in middleware that redirects to the access URL if
973997
// the request is not to a local IP.
974998
var handler http.Handler = coderAPI.RootHandler
@@ -1049,10 +1073,10 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
10491073
case <-stopCtx.Done():
10501074
exitErr = stopCtx.Err()
10511075
waitForProvisionerJobs = true
1052-
_, _ = io.WriteString(inv.Stdout, cliui.Bold("Stop caught, waiting for provisioner jobs to complete and gracefully exiting. Use ctrl+\\ to force quit"))
1076+
_, _ = io.WriteString(inv.Stdout, cliui.Bold("Stop caught, waiting for provisioner jobs to complete and gracefully exiting. Use ctrl+\\ to force quit\n"))
10531077
case <-interruptCtx.Done():
10541078
exitErr = interruptCtx.Err()
1055-
_, _ = io.WriteString(inv.Stdout, cliui.Bold("Interrupt caught, gracefully exiting. Use ctrl+\\ to force quit"))
1079+
_, _ = io.WriteString(inv.Stdout, cliui.Bold("Interrupt caught, gracefully exiting. Use ctrl+\\ to force quit\n"))
10561080
case <-tunnelDone:
10571081
exitErr = xerrors.New("dev tunnel closed unexpectedly")
10581082
case <-pubsubWatchdogTimeout:
@@ -1088,6 +1112,21 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
10881112
// Cancel any remaining in-flight requests.
10891113
shutdownConns()
10901114

1115+
if notificationsManager != nil {
1116+
// Stop the notification manager, which will cause any buffered updates to the store to be flushed.
1117+
// If the Stop() call times out, messages that were sent but not reflected as such in the store will have
1118+
// their leases expire after a period of time and will be re-queued for sending.
1119+
// See CODER_NOTIFICATIONS_LEASE_PERIOD.
1120+
cliui.Info(inv.Stdout, "Shutting down notifications manager..."+"\n")
1121+
err = shutdownWithTimeout(notificationsManager.Stop, 5*time.Second)
1122+
if err != nil {
1123+
cliui.Warnf(inv.Stderr, "Notifications manager shutdown took longer than 5s, "+
1124+
"this may result in duplicate notifications being sent: %s\n", err)
1125+
} else {
1126+
cliui.Info(inv.Stdout, "Gracefully shut down notifications manager\n")
1127+
}
1128+
}
1129+
10911130
// Shut down provisioners before waiting for WebSockets
10921131
// connections to close.
10931132
var wg sync.WaitGroup
@@ -1227,6 +1266,15 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
12271266
return serverCmd
12281267
}
12291268

1269+
// templateHelpers builds a set of functions which can be called in templates.
1270+
// We build them here to avoid an import cycle by using coderd.Options in notifications.Manager.
1271+
// We can later use this to inject whitelabel fields when app name / logo URL are overridden.
1272+
func templateHelpers(options *coderd.Options) map[string]any {
1273+
return map[string]any{
1274+
"base_url": func() string { return options.AccessURL.String() },
1275+
}
1276+
}
1277+
12301278
// printDeprecatedOptions loops through all command options, and prints
12311279
// a warning for usage of deprecated options.
12321280
func PrintDeprecatedOptions() serpent.MiddlewareFunc {

coderd/apidoc/docs.go

Lines changed: 79 additions & 0 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: 81 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)