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

Skip to content

Commit 056d1a5

Browse files
committed
Example usage
Signed-off-by: Danny Kopping <[email protected]>
1 parent 17603f5 commit 056d1a5

File tree

13 files changed

+2024
-1880
lines changed

13 files changed

+2024
-1880
lines changed

coderd/notifications/dispatch/smtp.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
"github.com/coder/coder/v2/coderd/notifications/render"
3131
"github.com/coder/coder/v2/coderd/notifications/types"
3232
markdown "github.com/coder/coder/v2/coderd/render"
33+
"github.com/coder/coder/v2/coderd/runtimeconfig"
3334
"github.com/coder/coder/v2/codersdk"
3435
)
3536

@@ -63,7 +64,7 @@ func NewSMTPHandler(cfg codersdk.NotificationsEmailConfig, helpers template.Func
6364
return &SMTPHandler{cfg: cfg, helpers: helpers, log: log}
6465
}
6566

66-
func (s *SMTPHandler) Dispatcher(payload types.MessagePayload, titleTmpl, bodyTmpl string) (DeliveryFunc, error) {
67+
func (s *SMTPHandler) Dispatcher(cfg runtimeconfig.Manager, payload types.MessagePayload, titleTmpl, bodyTmpl string) (DeliveryFunc, error) {
6768
// First render the subject & body into their own discrete strings.
6869
subject, err := markdown.PlaintextFromMarkdown(titleTmpl)
6970
if err != nil {

coderd/notifications/dispatch/smtp_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323

2424
"github.com/coder/coder/v2/coderd/notifications/dispatch"
2525
"github.com/coder/coder/v2/coderd/notifications/types"
26+
"github.com/coder/coder/v2/coderd/runtimeconfig"
2627
"github.com/coder/coder/v2/codersdk"
2728
"github.com/coder/coder/v2/testutil"
2829
)
@@ -486,7 +487,7 @@ func TestSMTP(t *testing.T) {
486487
Labels: make(map[string]string),
487488
}
488489

489-
dispatchFn, err := handler.Dispatcher(payload, subject, body)
490+
dispatchFn, err := handler.Dispatcher(runtimeconfig.NewNoopManager(), payload, subject, body)
490491
require.NoError(t, err)
491492

492493
msgID := uuid.New()

coderd/notifications/dispatch/webhook.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515

1616
"github.com/coder/coder/v2/coderd/notifications/types"
1717
markdown "github.com/coder/coder/v2/coderd/render"
18+
"github.com/coder/coder/v2/coderd/runtimeconfig"
1819
"github.com/coder/coder/v2/codersdk"
1920
)
2021

@@ -39,8 +40,13 @@ func NewWebhookHandler(cfg codersdk.NotificationsWebhookConfig, log slog.Logger)
3940
return &WebhookHandler{cfg: cfg, log: log, cl: &http.Client{}}
4041
}
4142

42-
func (w *WebhookHandler) Dispatcher(payload types.MessagePayload, titleTmpl, bodyTmpl string) (DeliveryFunc, error) {
43-
if w.cfg.Endpoint.String() == "" {
43+
func (w *WebhookHandler) Dispatcher(cfg runtimeconfig.Manager, payload types.MessagePayload, titleTmpl, bodyTmpl string) (DeliveryFunc, error) {
44+
endpoint, err := w.cfg.Endpoint.Coalesce(context.Background(), cfg)
45+
if err != nil {
46+
return nil, xerrors.Errorf("resolve endpoint value: %w", err)
47+
}
48+
49+
if endpoint.String() == "" {
4450
return nil, xerrors.New("webhook endpoint not defined")
4551
}
4652

@@ -53,7 +59,7 @@ func (w *WebhookHandler) Dispatcher(payload types.MessagePayload, titleTmpl, bod
5359
return nil, xerrors.Errorf("render body: %w", err)
5460
}
5561

56-
return w.dispatch(payload, title, body, w.cfg.Endpoint.String()), nil
62+
return w.dispatch(payload, title, body, endpoint.String()), nil
5763
}
5864

5965
func (w *WebhookHandler) dispatch(msgPayload types.MessagePayload, title, body, endpoint string) DeliveryFunc {

coderd/notifications/dispatch/webhook_test.go

Lines changed: 71 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,20 @@ import (
1010
"testing"
1111
"time"
1212

13+
"github.com/coder/serpent"
1314
"github.com/google/uuid"
1415
"github.com/stretchr/testify/assert"
1516
"github.com/stretchr/testify/require"
1617

1718
"cdr.dev/slog"
1819
"cdr.dev/slog/sloggers/slogtest"
19-
"github.com/coder/serpent"
2020

21+
"github.com/coder/coder/v2/coderd"
22+
"github.com/coder/coder/v2/coderd/coderdtest"
23+
"github.com/coder/coder/v2/coderd/database/dbmem"
2124
"github.com/coder/coder/v2/coderd/notifications/dispatch"
2225
"github.com/coder/coder/v2/coderd/notifications/types"
26+
"github.com/coder/coder/v2/coderd/runtimeconfig"
2327
"github.com/coder/coder/v2/codersdk"
2428
"github.com/coder/coder/v2/testutil"
2529
)
@@ -67,14 +71,6 @@ func TestWebhook(t *testing.T) {
6771
},
6872
expectSuccess: true,
6973
},
70-
{
71-
name: "invalid endpoint",
72-
// Build a deliberately invalid URL to fail validation.
73-
serverURL: "invalid .com",
74-
expectSuccess: false,
75-
expectErr: "invalid URL escape",
76-
expectRetryable: false,
77-
},
7874
{
7975
name: "timeout",
8076
serverDeadline: time.Now().Add(-time.Hour),
@@ -134,11 +130,11 @@ func TestWebhook(t *testing.T) {
134130
require.NoError(t, err)
135131
}
136132

137-
cfg := codersdk.NotificationsWebhookConfig{
138-
Endpoint: *serpent.URLOf(endpoint),
139-
}
140-
handler := dispatch.NewWebhookHandler(cfg, logger.With(slog.F("test", tc.name)))
141-
deliveryFn, err := handler.Dispatcher(msgPayload, titleTemplate, bodyTemplate)
133+
vals := coderdtest.DeploymentValues(t, func(values *codersdk.DeploymentValues) {
134+
require.NoError(t, values.Notifications.Webhook.Endpoint.Set(endpoint.String()))
135+
})
136+
handler := dispatch.NewWebhookHandler(vals.Notifications.Webhook, logger.With(slog.F("test", tc.name)))
137+
deliveryFn, err := handler.Dispatcher(runtimeconfig.NewNoopManager(), msgPayload, titleTemplate, bodyTemplate)
142138
require.NoError(t, err)
143139

144140
retryable, err := deliveryFn(ctx, msgID)
@@ -153,3 +149,64 @@ func TestWebhook(t *testing.T) {
153149
})
154150
}
155151
}
152+
153+
func TestRuntimeEndpointChange(t *testing.T) {
154+
t.Parallel()
155+
156+
ctx := testutil.Context(t, testutil.WaitShort)
157+
158+
const (
159+
titleTemplate = "this is the title ({{.Labels.foo}})"
160+
bodyTemplate = "this is the body ({{.Labels.baz}})"
161+
162+
startEndpoint = "http://localhost:0"
163+
)
164+
165+
msgPayload := types.MessagePayload{
166+
Version: "1.0",
167+
NotificationName: "test",
168+
Labels: map[string]string{
169+
"foo": "bar",
170+
"baz": "quux",
171+
},
172+
}
173+
174+
// Setup: start up a mock HTTP server
175+
received := make(chan *http.Request, 1)
176+
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
177+
received <- r
178+
close(received)
179+
}))
180+
t.Cleanup(server.Close)
181+
182+
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true}).Leveled(slog.LevelDebug)
183+
184+
runtimeEndpoint, err := url.Parse(server.URL)
185+
require.NoError(t, err)
186+
_ = runtimeEndpoint
187+
188+
// Initially, set the endpoint to a hostport we know to not be listening for HTTP requests.
189+
vals := coderdtest.DeploymentValues(t, func(values *codersdk.DeploymentValues) {
190+
require.NoError(t, values.Notifications.Webhook.Endpoint.Set(startEndpoint))
191+
})
192+
193+
// Setup runtime config manager.
194+
mgr := coderd.NewRuntimeConfigStore(dbmem.New())
195+
196+
// Dispatch a notification and it will fail.
197+
handler := dispatch.NewWebhookHandler(vals.Notifications.Webhook, logger.With(slog.F("test", t.Name())))
198+
deliveryFn, err := handler.Dispatcher(mgr, msgPayload, titleTemplate, bodyTemplate)
199+
require.NoError(t, err)
200+
201+
msgID := uuid.New()
202+
_, err = deliveryFn(ctx, msgID)
203+
require.ErrorContains(t, err, "can't assign requested address")
204+
205+
// Set the runtime value to the mock HTTP server.
206+
require.NoError(t, vals.Notifications.Webhook.Endpoint.SetRuntimeValue(ctx, mgr, serpent.URLOf(runtimeEndpoint)))
207+
deliveryFn, err = handler.Dispatcher(mgr, msgPayload, titleTemplate, bodyTemplate)
208+
require.NoError(t, err)
209+
_, err = deliveryFn(ctx, msgID)
210+
require.NoError(t, err)
211+
testutil.RequireRecvCtx(ctx, t, received)
212+
}

coderd/notifications/manager.go

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515

1616
"github.com/coder/coder/v2/coderd/database"
1717
"github.com/coder/coder/v2/coderd/notifications/dispatch"
18+
"github.com/coder/coder/v2/coderd/runtimeconfig"
1819
"github.com/coder/coder/v2/codersdk"
1920
)
2021

@@ -38,7 +39,8 @@ var ErrInvalidDispatchTimeout = xerrors.New("dispatch timeout must be less than
3839
// we split notifiers out into separate targets for greater processing throughput; in this case we will need an
3940
// alternative mechanism for handling backpressure.
4041
type Manager struct {
41-
cfg codersdk.NotificationsConfig
42+
cfg codersdk.NotificationsConfig
43+
runtimeCfg runtimeconfig.Manager
4244

4345
store Store
4446
log slog.Logger
@@ -69,6 +71,16 @@ func WithTestClock(clock quartz.Clock) ManagerOption {
6971
}
7072
}
7173

74+
func WithRuntimeConfigManager(mgr runtimeconfig.Manager) ManagerOption {
75+
if mgr == nil {
76+
panic("developer error: runtime config manager is nil")
77+
}
78+
79+
return func(m *Manager) {
80+
m.runtimeCfg = mgr
81+
}
82+
}
83+
7284
// NewManager instantiates a new Manager instance which coordinates notification enqueuing and delivery.
7385
//
7486
// helpers is a map of template helpers which are used to customize notification messages to use global settings like
@@ -88,9 +100,11 @@ func NewManager(cfg codersdk.NotificationsConfig, store Store, helpers template.
88100
}
89101

90102
m := &Manager{
91-
log: log,
92-
cfg: cfg,
93-
store: store,
103+
log: log,
104+
105+
cfg: cfg,
106+
runtimeCfg: runtimeconfig.NewNoopManager(),
107+
store: store,
94108

95109
// Buffer successful/failed notification dispatches in memory to reduce load on the store.
96110
//
@@ -169,7 +183,7 @@ func (m *Manager) loop(ctx context.Context) error {
169183
var eg errgroup.Group
170184

171185
// Create a notifier to run concurrently, which will handle dequeueing and dispatching notifications.
172-
m.notifier = newNotifier(m.cfg, uuid.New(), m.log, m.store, m.handlers, m.metrics, m.clock)
186+
m.notifier = newNotifier(m.cfg, uuid.New(), m.log, m.store, m.runtimeCfg, m.handlers, m.metrics, m.clock)
173187
eg.Go(func() error {
174188
return m.notifier.run(ctx, m.success, m.failure)
175189
})

coderd/notifications/manager_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"github.com/coder/coder/v2/coderd/notifications"
2323
"github.com/coder/coder/v2/coderd/notifications/dispatch"
2424
"github.com/coder/coder/v2/coderd/notifications/types"
25+
"github.com/coder/coder/v2/coderd/runtimeconfig"
2526
"github.com/coder/coder/v2/testutil"
2627
)
2728

@@ -205,7 +206,7 @@ type santaHandler struct {
205206
nice atomic.Int32
206207
}
207208

208-
func (s *santaHandler) Dispatcher(payload types.MessagePayload, _, _ string) (dispatch.DeliveryFunc, error) {
209+
func (s *santaHandler) Dispatcher(cfg runtimeconfig.Manager, payload types.MessagePayload, _, _ string) (dispatch.DeliveryFunc, error) {
209210
return func(ctx context.Context, msgID uuid.UUID) (retryable bool, err error) {
210211
if payload.Labels["nice"] != "true" {
211212
s.naughty.Add(1)

coderd/notifications/metrics_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"github.com/coder/coder/v2/coderd/notifications"
2626
"github.com/coder/coder/v2/coderd/notifications/dispatch"
2727
"github.com/coder/coder/v2/coderd/notifications/types"
28+
"github.com/coder/coder/v2/coderd/runtimeconfig"
2829
"github.com/coder/coder/v2/testutil"
2930
)
3031

@@ -506,8 +507,8 @@ func newDelayingHandler(delay time.Duration, handler notifications.Handler) *del
506507
}
507508
}
508509

509-
func (d *delayingHandler) Dispatcher(payload types.MessagePayload, title, body string) (dispatch.DeliveryFunc, error) {
510-
deliverFn, err := d.h.Dispatcher(payload, title, body)
510+
func (d *delayingHandler) Dispatcher(cfg runtimeconfig.Manager, payload types.MessagePayload, title, body string) (dispatch.DeliveryFunc, error) {
511+
deliverFn, err := d.h.Dispatcher(runtimeconfig.NewNoopManager(), payload, title, body)
511512
if err != nil {
512513
return nil, err
513514
}

coderd/notifications/notifications_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import (
4141
"github.com/coder/coder/v2/coderd/notifications/render"
4242
"github.com/coder/coder/v2/coderd/notifications/types"
4343
"github.com/coder/coder/v2/coderd/rbac"
44+
"github.com/coder/coder/v2/coderd/runtimeconfig"
4445
"github.com/coder/coder/v2/coderd/util/syncmap"
4546
"github.com/coder/coder/v2/codersdk"
4647
"github.com/coder/coder/v2/testutil"
@@ -1167,7 +1168,7 @@ type fakeHandler struct {
11671168
succeeded, failed []string
11681169
}
11691170

1170-
func (f *fakeHandler) Dispatcher(payload types.MessagePayload, _, _ string) (dispatch.DeliveryFunc, error) {
1171+
func (f *fakeHandler) Dispatcher(cfg runtimeconfig.Manager, payload types.MessagePayload, _, _ string) (dispatch.DeliveryFunc, error) {
11711172
return func(_ context.Context, msgID uuid.UUID) (retryable bool, err error) {
11721173
f.mu.Lock()
11731174
defer f.mu.Unlock()

coderd/notifications/notifier.go

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,14 @@ import (
99
"golang.org/x/sync/errgroup"
1010
"golang.org/x/xerrors"
1111

12+
"github.com/coder/quartz"
13+
1214
"github.com/coder/coder/v2/coderd/database/dbtime"
1315
"github.com/coder/coder/v2/coderd/notifications/dispatch"
1416
"github.com/coder/coder/v2/coderd/notifications/render"
1517
"github.com/coder/coder/v2/coderd/notifications/types"
18+
"github.com/coder/coder/v2/coderd/runtimeconfig"
1619
"github.com/coder/coder/v2/codersdk"
17-
"github.com/coder/quartz"
1820

1921
"cdr.dev/slog"
2022

@@ -24,8 +26,10 @@ import (
2426
// notifier is a consumer of the notifications_messages queue. It dequeues messages from that table and processes them
2527
// through a pipeline of fetch -> prepare -> render -> acquire handler -> deliver.
2628
type notifier struct {
27-
id uuid.UUID
28-
cfg codersdk.NotificationsConfig
29+
id uuid.UUID
30+
cfg codersdk.NotificationsConfig
31+
runtimeCfg runtimeconfig.Manager
32+
2933
log slog.Logger
3034
store Store
3135

@@ -41,21 +45,21 @@ type notifier struct {
4145
clock quartz.Clock
4246
}
4347

44-
func newNotifier(cfg codersdk.NotificationsConfig, id uuid.UUID, log slog.Logger, db Store,
45-
hr map[database.NotificationMethod]Handler, metrics *Metrics, clock quartz.Clock,
46-
) *notifier {
48+
func newNotifier(cfg codersdk.NotificationsConfig, id uuid.UUID, log slog.Logger, db Store, runtimeCfg runtimeconfig.Manager,
49+
hr map[database.NotificationMethod]Handler, metrics *Metrics, clock quartz.Clock) *notifier {
4750
tick := clock.NewTicker(cfg.FetchInterval.Value(), "notifier", "fetchInterval")
4851
return &notifier{
49-
id: id,
50-
cfg: cfg,
51-
log: log.Named("notifier").With(slog.F("notifier_id", id)),
52-
quit: make(chan any),
53-
done: make(chan any),
54-
tick: tick,
55-
store: db,
56-
handlers: hr,
57-
metrics: metrics,
58-
clock: clock,
52+
id: id,
53+
cfg: cfg,
54+
runtimeCfg: runtimeCfg,
55+
log: log.Named("notifier").With(slog.F("notifier_id", id)),
56+
quit: make(chan any),
57+
done: make(chan any),
58+
tick: tick,
59+
store: db,
60+
handlers: hr,
61+
metrics: metrics,
62+
clock: clock,
5963
}
6064
}
6165

@@ -228,7 +232,7 @@ func (n *notifier) prepare(ctx context.Context, msg database.AcquireNotification
228232
return nil, xerrors.Errorf("render body: %w", err)
229233
}
230234

231-
return handler.Dispatcher(payload, title, body)
235+
return handler.Dispatcher(runtimeconfig.NewNoopManager(), payload, title, body)
232236
}
233237

234238
// deliver sends a given notification message via its defined method.

coderd/notifications/spec.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"github.com/coder/coder/v2/coderd/database"
99
"github.com/coder/coder/v2/coderd/notifications/dispatch"
1010
"github.com/coder/coder/v2/coderd/notifications/types"
11+
"github.com/coder/coder/v2/coderd/runtimeconfig"
1112
)
1213

1314
// Store defines the API between the notifications system and the storage.
@@ -27,7 +28,7 @@ type Store interface {
2728
// Handler is responsible for preparing and delivering a notification by a given method.
2829
type Handler interface {
2930
// Dispatcher constructs a DeliveryFunc to be used for delivering a notification via the chosen method.
30-
Dispatcher(payload types.MessagePayload, title, body string) (dispatch.DeliveryFunc, error)
31+
Dispatcher(cfg runtimeconfig.Manager, payload types.MessagePayload, title, body string) (dispatch.DeliveryFunc, error)
3132
}
3233

3334
// Enqueuer enqueues a new notification message in the store and returns its ID, should it enqueue without failure.

0 commit comments

Comments
 (0)