diff --git a/cli/notifications.go b/cli/notifications.go
new file mode 100644
index 0000000000000..055a4bfa65e3b
--- /dev/null
+++ b/cli/notifications.go
@@ -0,0 +1,85 @@
+package cli
+
+import (
+ "fmt"
+
+ "golang.org/x/xerrors"
+
+ "github.com/coder/serpent"
+
+ "github.com/coder/coder/v2/codersdk"
+)
+
+func (r *RootCmd) notifications() *serpent.Command {
+ cmd := &serpent.Command{
+ Use: "notifications",
+ Short: "Manage Coder notifications",
+ Long: "Administrators can use these commands to change notification settings.\n" + FormatExamples(
+ Example{
+ Description: "Pause Coder notifications. Administrators can temporarily stop notifiers from dispatching messages in case of the target outage (for example: unavailable SMTP server or Webhook not responding).",
+ Command: "coder notifications pause",
+ },
+ Example{
+ Description: "Resume Coder notifications",
+ Command: "coder notifications resume",
+ },
+ ),
+ Aliases: []string{"notification"},
+ Handler: func(inv *serpent.Invocation) error {
+ return inv.Command.HelpHandler(inv)
+ },
+ Children: []*serpent.Command{
+ r.pauseNotifications(),
+ r.resumeNotifications(),
+ },
+ }
+ return cmd
+}
+
+func (r *RootCmd) pauseNotifications() *serpent.Command {
+ client := new(codersdk.Client)
+ cmd := &serpent.Command{
+ Use: "pause",
+ Short: "Pause notifications",
+ Middleware: serpent.Chain(
+ serpent.RequireNArgs(0),
+ r.InitClient(client),
+ ),
+ Handler: func(inv *serpent.Invocation) error {
+ err := client.PutNotificationsSettings(inv.Context(), codersdk.NotificationsSettings{
+ NotifierPaused: true,
+ })
+ if err != nil {
+ return xerrors.Errorf("unable to pause notifications: %w", err)
+ }
+
+ _, _ = fmt.Fprintln(inv.Stderr, "Notifications are now paused.")
+ return nil
+ },
+ }
+ return cmd
+}
+
+func (r *RootCmd) resumeNotifications() *serpent.Command {
+ client := new(codersdk.Client)
+ cmd := &serpent.Command{
+ Use: "resume",
+ Short: "Resume notifications",
+ Middleware: serpent.Chain(
+ serpent.RequireNArgs(0),
+ r.InitClient(client),
+ ),
+ Handler: func(inv *serpent.Invocation) error {
+ err := client.PutNotificationsSettings(inv.Context(), codersdk.NotificationsSettings{
+ NotifierPaused: false,
+ })
+ if err != nil {
+ return xerrors.Errorf("unable to resume notifications: %w", err)
+ }
+
+ _, _ = fmt.Fprintln(inv.Stderr, "Notifications are now resumed.")
+ return nil
+ },
+ }
+ return cmd
+}
diff --git a/cli/notifications_test.go b/cli/notifications_test.go
new file mode 100644
index 0000000000000..9ea4d7072e4c3
--- /dev/null
+++ b/cli/notifications_test.go
@@ -0,0 +1,102 @@
+package cli_test
+
+import (
+ "bytes"
+ "context"
+ "encoding/json"
+ "net/http"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+
+ "github.com/coder/coder/v2/cli/clitest"
+ "github.com/coder/coder/v2/coderd/coderdtest"
+ "github.com/coder/coder/v2/codersdk"
+ "github.com/coder/coder/v2/testutil"
+)
+
+func TestNotifications(t *testing.T) {
+ t.Parallel()
+
+ tests := []struct {
+ name string
+ command string
+ expectPaused bool
+ }{
+ {
+ name: "PauseNotifications",
+ command: "pause",
+ expectPaused: true,
+ },
+ {
+ name: "ResumeNotifications",
+ command: "resume",
+ expectPaused: false,
+ },
+ }
+
+ for _, tt := range tests {
+ tt := tt
+ t.Run(tt.name, func(t *testing.T) {
+ t.Parallel()
+
+ // given
+ ownerClient, db := coderdtest.NewWithDatabase(t, nil)
+ _ = coderdtest.CreateFirstUser(t, ownerClient)
+
+ // when
+ inv, root := clitest.New(t, "notifications", tt.command)
+ clitest.SetupConfig(t, ownerClient, root)
+
+ var buf bytes.Buffer
+ inv.Stdout = &buf
+ err := inv.Run()
+ require.NoError(t, err)
+
+ // then
+ ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort)
+ t.Cleanup(cancel)
+ settingsJSON, err := db.GetNotificationsSettings(ctx)
+ require.NoError(t, err)
+
+ var settings codersdk.NotificationsSettings
+ err = json.Unmarshal([]byte(settingsJSON), &settings)
+ require.NoError(t, err)
+ require.Equal(t, tt.expectPaused, settings.NotifierPaused)
+ })
+ }
+}
+
+func TestPauseNotifications_RegularUser(t *testing.T) {
+ t.Parallel()
+
+ // given
+ ownerClient, db := coderdtest.NewWithDatabase(t, nil)
+ owner := coderdtest.CreateFirstUser(t, ownerClient)
+ anotherClient, _ := coderdtest.CreateAnotherUser(t, ownerClient, owner.OrganizationID)
+
+ // when
+ inv, root := clitest.New(t, "notifications", "pause")
+ clitest.SetupConfig(t, anotherClient, root)
+
+ var buf bytes.Buffer
+ inv.Stdout = &buf
+ err := inv.Run()
+ var sdkError *codersdk.Error
+ require.Error(t, err)
+ require.ErrorAsf(t, err, &sdkError, "error should be of type *codersdk.Error")
+ assert.Equal(t, http.StatusForbidden, sdkError.StatusCode())
+ assert.Contains(t, sdkError.Message, "Insufficient permissions to update notifications settings.")
+
+ // then
+ ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort)
+ t.Cleanup(cancel)
+ settingsJSON, err := db.GetNotificationsSettings(ctx)
+ require.NoError(t, err)
+
+ var settings codersdk.NotificationsSettings
+ err = json.Unmarshal([]byte(settingsJSON), &settings)
+ require.NoError(t, err)
+ require.False(t, settings.NotifierPaused) // still running
+}
diff --git a/cli/root.go b/cli/root.go
index 4e615eaa6e7af..579f3c4c29202 100644
--- a/cli/root.go
+++ b/cli/root.go
@@ -87,6 +87,8 @@ func (r *RootCmd) CoreSubcommands() []*serpent.Command {
r.login(),
r.logout(),
r.netcheck(),
+ r.notifications(),
+ r.organizations(),
r.portForward(),
r.publickey(),
r.resetPassword(),
@@ -95,7 +97,6 @@ func (r *RootCmd) CoreSubcommands() []*serpent.Command {
r.tokens(),
r.users(),
r.version(defaultVersionInfo),
- r.organizations(),
// Workspace Commands
r.autoupdate(),
@@ -120,11 +121,11 @@ func (r *RootCmd) CoreSubcommands() []*serpent.Command {
r.whoami(),
// Hidden
+ r.expCmd(),
r.gitssh(),
+ r.support(),
r.vscodeSSH(),
r.workspaceAgent(),
- r.expCmd(),
- r.support(),
}
}
diff --git a/cli/testdata/coder_--help.golden b/cli/testdata/coder_--help.golden
index a576797d8a48d..494ed7decb492 100644
--- a/cli/testdata/coder_--help.golden
+++ b/cli/testdata/coder_--help.golden
@@ -27,6 +27,7 @@ SUBCOMMANDS:
login Authenticate with Coder deployment
logout Unauthenticate your local session
netcheck Print network debug information for DERP and STUN
+ notifications Manage Coder notifications
open Open a workspace
ping Ping a workspace
port-forward Forward ports from a workspace to the local machine. For
diff --git a/cli/testdata/coder_notifications_--help.golden b/cli/testdata/coder_notifications_--help.golden
new file mode 100644
index 0000000000000..b54e98543da7b
--- /dev/null
+++ b/cli/testdata/coder_notifications_--help.golden
@@ -0,0 +1,28 @@
+coder v0.0.0-devel
+
+USAGE:
+ coder notifications
+
+ Manage Coder notifications
+
+ Aliases: notification
+
+ Administrators can use these commands to change notification settings.
+ - Pause Coder notifications. Administrators can temporarily stop notifiers
+ from
+ dispatching messages in case of the target outage (for example: unavailable
+ SMTP
+ server or Webhook not responding).:
+
+ $ coder notifications pause
+
+ - Resume Coder notifications:
+
+ $ coder notifications resume
+
+SUBCOMMANDS:
+ pause Pause notifications
+ resume Resume notifications
+
+———
+Run `coder --help` for a list of global options.
diff --git a/cli/testdata/coder_notifications_pause_--help.golden b/cli/testdata/coder_notifications_pause_--help.golden
new file mode 100644
index 0000000000000..fc3f2621ad788
--- /dev/null
+++ b/cli/testdata/coder_notifications_pause_--help.golden
@@ -0,0 +1,9 @@
+coder v0.0.0-devel
+
+USAGE:
+ coder notifications pause
+
+ Pause notifications
+
+———
+Run `coder --help` for a list of global options.
diff --git a/cli/testdata/coder_notifications_resume_--help.golden b/cli/testdata/coder_notifications_resume_--help.golden
new file mode 100644
index 0000000000000..ea69e1e789a2e
--- /dev/null
+++ b/cli/testdata/coder_notifications_resume_--help.golden
@@ -0,0 +1,9 @@
+coder v0.0.0-devel
+
+USAGE:
+ coder notifications resume
+
+ Resume notifications
+
+———
+Run `coder --help` for a list of global options.
diff --git a/docs/cli.md b/docs/cli.md
index f38bc0e3e133a..ab97ca9cc4d10 100644
--- a/docs/cli.md
+++ b/docs/cli.md
@@ -30,6 +30,7 @@ Coder — A tool for provisioning self-hosted development environments with Terr
| [login
](./cli/login.md) | Authenticate with Coder deployment |
| [logout
](./cli/logout.md) | Unauthenticate your local session |
| [netcheck
](./cli/netcheck.md) | Print network debug information for DERP and STUN |
+| [notifications
](./cli/notifications.md) | Manage Coder notifications |
| [port-forward
](./cli/port-forward.md) | Forward ports from a workspace to the local machine. For reverse port forwarding, use "coder ssh -R". |
| [publickey
](./cli/publickey.md) | Output your Coder public key used for Git operations |
| [reset-password
](./cli/reset-password.md) | Directly connect to the database to reset a user's password |
diff --git a/docs/cli/notifications.md b/docs/cli/notifications.md
new file mode 100644
index 0000000000000..59e74b4324357
--- /dev/null
+++ b/docs/cli/notifications.md
@@ -0,0 +1,37 @@
+
+
+# notifications
+
+Manage Coder notifications
+
+Aliases:
+
+- notification
+
+## Usage
+
+```console
+coder notifications
+```
+
+## Description
+
+```console
+Administrators can use these commands to change notification settings.
+ - Pause Coder notifications. Administrators can temporarily stop notifiers from
+dispatching messages in case of the target outage (for example: unavailable SMTP
+server or Webhook not responding).:
+
+ $ coder notifications pause
+
+ - Resume Coder notifications:
+
+ $ coder notifications resume
+```
+
+## Subcommands
+
+| Name | Purpose |
+| ------------------------------------------------ | -------------------- |
+| [pause
](./notifications_pause.md) | Pause notifications |
+| [resume
](./notifications_resume.md) | Resume notifications |
diff --git a/docs/cli/notifications_pause.md b/docs/cli/notifications_pause.md
new file mode 100644
index 0000000000000..0cb2b101d474c
--- /dev/null
+++ b/docs/cli/notifications_pause.md
@@ -0,0 +1,11 @@
+
+
+# notifications pause
+
+Pause notifications
+
+## Usage
+
+```console
+coder notifications pause
+```
diff --git a/docs/cli/notifications_resume.md b/docs/cli/notifications_resume.md
new file mode 100644
index 0000000000000..a8dc17453a383
--- /dev/null
+++ b/docs/cli/notifications_resume.md
@@ -0,0 +1,11 @@
+
+
+# notifications resume
+
+Resume notifications
+
+## Usage
+
+```console
+coder notifications resume
+```
diff --git a/docs/manifest.json b/docs/manifest.json
index dc887921b2b20..82dd73ada47c8 100644
--- a/docs/manifest.json
+++ b/docs/manifest.json
@@ -755,6 +755,21 @@
"description": "Print network debug information for DERP and STUN",
"path": "cli/netcheck.md"
},
+ {
+ "title": "notifications",
+ "description": "Manage Coder notifications",
+ "path": "cli/notifications.md"
+ },
+ {
+ "title": "notifications pause",
+ "description": "Pause notifications",
+ "path": "cli/notifications_pause.md"
+ },
+ {
+ "title": "notifications resume",
+ "description": "Resume notifications",
+ "path": "cli/notifications_resume.md"
+ },
{
"title": "open",
"description": "Open a workspace",