Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sending the Jobs notifications to Lark channels #263

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open
9 changes: 6 additions & 3 deletions config/config_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,12 @@ type TelemetryConfig struct {
}

type AlertingConfig struct {
EventManager EventManagerConfig `mapstructure:"alert_manager"`
Dashboard string `mapstructure:"dashboard"`
DataConsole string `mapstructure:"data_console"`
EventManager EventManagerConfig `mapstructure:"alert_manager"`
Dashboard string `mapstructure:"dashboard"`
DataConsole string `mapstructure:"data_console"`
EnableLarkNotifications bool `mapstructure:"enable_lark_notification"`
LarkSLAMissTemplate string `mapstructure:"lark_sla_miss_template"`
LarkFailureTemplate string `mapstructure:"lark_failure_template"`
}

type EventManagerConfig struct {
Expand Down
9 changes: 9 additions & 0 deletions core/scheduler/job_run.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,15 @@ type NotifyAttrs struct {
Secret string
}

type LarkNotifyAttrs struct {
Owner string
JobEvent *Event
Route string
AppID string
AppSecret string
VerificationToken string
}

const (
MetricNotificationQueue = "notification_queue_total"
MetricNotificationWorkerBatch = "notification_worker_batch_total"
Expand Down
1 change: 1 addition & 0 deletions core/scheduler/larkEvent.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package scheduler
37 changes: 36 additions & 1 deletion core/scheduler/service/events_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ type Webhook interface {
Trigger(attr scheduler.WebhookAttrs)
}

type LarkNotifier interface {
io.Closer
Notify(ctx context.Context, attr scheduler.LarkNotifyAttrs) error
}

type AlertManager interface {
io.Closer
Relay(attr *scheduler.AlertAttrs)
Expand All @@ -38,6 +43,7 @@ type AlertManager interface {
type EventsService struct {
notifyChannels map[string]Notifier
webhookChannel Webhook
larkNotifier LarkNotifier
alertManager AlertManager
compiler TemplateCompiler
jobRepo JobRepository
Expand Down Expand Up @@ -177,6 +183,34 @@ func (e *EventsService) Push(ctx context.Context, event *scheduler.Event) error
e.l.Error("Error: No notification event for job current error: %s", currErr)
multierror.Append(fmt.Errorf("notifyChannel.Notify: %s: %w", channel, currErr))
}
// This will send the notification to lark aswell
if scheme == NotificationSchemeSlack && e.larkNotifier != nil {
appid, err := secretMap.Get(tenant.SecretLarkAppID)
if err != nil {
return err
}

appSecret, err := secretMap.Get(tenant.SecretLarkAppSecret)
if err != nil {
return err
}

appVerificationToken, err := secretMap.Get(tenant.SecretLarkVerificationToken)
if err != nil {
return err
}
err = e.larkNotifier.Notify(ctx, scheduler.LarkNotifyAttrs{
Owner: jobDetails.JobMetadata.Owner,
JobEvent: event,
Route: route,
AppID: appid,
AppSecret: appSecret,
VerificationToken: appVerificationToken,
})
if err != nil {
return err
}
}
}
}
telemetry.NewCounter("jobrun_alerts_total", map[string]string{
Expand All @@ -200,13 +234,14 @@ func (e *EventsService) Close() error {
return me.ToErr()
}

func NewEventsService(l log.Logger, jobRepo JobRepository, tenantService TenantService, notifyChan map[string]Notifier, webhookNotifier Webhook, compiler TemplateCompiler, alertsHandler AlertManager) *EventsService {
func NewEventsService(l log.Logger, jobRepo JobRepository, tenantService TenantService, notifyChan map[string]Notifier, webhookNotifier Webhook, larkNotifier LarkNotifier, compiler TemplateCompiler, alertsHandler AlertManager) *EventsService {
return &EventsService{
l: l,
jobRepo: jobRepo,
tenantService: tenantService,
notifyChannels: notifyChan,
webhookChannel: webhookNotifier,
larkNotifier: larkNotifier,
compiler: compiler,
alertManager: alertsHandler,
}
Expand Down
77 changes: 71 additions & 6 deletions core/scheduler/service/events_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func TestNotificationService(t *testing.T) {
jobRepo.On("GetJobDetails", ctx, project.Name(), jobName).Return(nil, fmt.Errorf("some error"))
defer jobRepo.AssertExpectations(t)

notifyService := service.NewEventsService(logger, jobRepo, nil, nil, nil, nil, nil)
notifyService := service.NewEventsService(logger, jobRepo, nil, nil, nil, nil, nil, nil)

event := &scheduler.Event{
JobName: jobName,
Expand Down Expand Up @@ -123,7 +123,7 @@ func TestNotificationService(t *testing.T) {
templateCompiler.On("Compile", mock.Anything, secretContext).Return(map[string]string{"header": "headerValue"}, nil)
defer templateCompiler.AssertExpectations(t)

notifyService := service.NewEventsService(logger, jobRepo, tenantService, nil, webhookChannel, templateCompiler, nil)
notifyService := service.NewEventsService(logger, jobRepo, tenantService, nil, webhookChannel, nil, templateCompiler, nil)

err := notifyService.Webhook(ctx, event)
assert.Nil(t, err)
Expand All @@ -148,7 +148,7 @@ func TestNotificationService(t *testing.T) {
tenantService.On("GetDetails", ctx, tnnt).Return(tenantWithDetails, nil)
defer tenantService.AssertExpectations(t)

notifyService := service.NewEventsService(logger, jobRepo, tenantService, nil, nil, nil, alertManager)
notifyService := service.NewEventsService(logger, jobRepo, tenantService, nil, nil, nil, nil, alertManager)

err := notifyService.Relay(ctx, event)
assert.Nil(t, err)
Expand All @@ -159,8 +159,57 @@ func TestNotificationService(t *testing.T) {
jobRepo.On("GetJobDetails", ctx, project.Name(), jobName).Return(&jobWithDetails, nil)
defer jobRepo.AssertExpectations(t)

plainSecret, _ := tenant.NewPlainTextSecret("NOTIFY_SLACK", "secretValue")
larkAppID, _ := tenant.NewPlainTextSecret(tenant.SecretLarkAppID, "secretValue1")
larkAppIDSecret, _ := tenant.NewPlainTextSecret(tenant.SecretLarkAppSecret, "secretValue2")
larkAppVerificationToken, _ := tenant.NewPlainTextSecret(tenant.SecretLarkVerificationToken, "secretValue3")
plainSecrets := []*tenant.PlainTextSecret{plainSecret, larkAppID, larkAppIDSecret, larkAppVerificationToken}
tenantService := new(mockTenantService)
tenantService.On("GetSecrets", ctx, tnnt).Return(plainSecrets, nil)
defer tenantService.AssertExpectations(t)

notifyChanelSlack := new(mockNotificationChanel)
notifyChanelSlack.On("Notify", ctx, scheduler.NotifyAttrs{
Owner: "jobOwnerName",
JobEvent: event,
Route: "#chanel-name",
Secret: "secretValue",
}).Return(nil)
defer notifyChanelSlack.AssertExpectations(t)
notifyChanelPager := new(mockNotificationChanel)
defer notifyChanelPager.AssertExpectations(t)

larkNotifyChannel := new(mockLarkNotificationChanel)

larkNotifyChannel.On("Notify", ctx, scheduler.LarkNotifyAttrs{
Owner: "jobOwnerName",
JobEvent: event,
Route: "#chanel-name",
AppID: "secretValue1",
AppSecret: "secretValue2",
VerificationToken: "secretValue3",
}).Return(nil)

defer larkNotifyChannel.AssertExpectations(t)

notifierChannels := map[string]service.Notifier{
"slack": notifyChanelSlack,
"pagerduty": notifyChanelPager,
}

notifyService := service.NewEventsService(logger, jobRepo, tenantService, notifierChannels, nil, larkNotifyChannel, nil, nil)

err := notifyService.Push(ctx, event)
assert.Nil(t, err)
})

t.Run("should send slack notification when there are no secrets added for lark", func(t *testing.T) {
jobRepo := new(JobRepository)
jobRepo.On("GetJobDetails", ctx, project.Name(), jobName).Return(&jobWithDetails, nil)
defer jobRepo.AssertExpectations(t)
plainSecret, _ := tenant.NewPlainTextSecret("NOTIFY_SLACK", "secretValue")
plainSecrets := []*tenant.PlainTextSecret{plainSecret}

tenantService := new(mockTenantService)
tenantService.On("GetSecrets", ctx, tnnt).Return(plainSecrets, nil)
defer tenantService.AssertExpectations(t)
Expand All @@ -181,7 +230,7 @@ func TestNotificationService(t *testing.T) {
"pagerduty": notifyChanelPager,
}

notifyService := service.NewEventsService(logger, jobRepo, tenantService, notifierChannels, nil, nil, nil)
notifyService := service.NewEventsService(logger, jobRepo, tenantService, notifierChannels, nil, nil, nil, nil)

err := notifyService.Push(ctx, event)
assert.Nil(t, err)
Expand Down Expand Up @@ -244,7 +293,7 @@ func TestNotificationService(t *testing.T) {
"pagerduty": notifyChanelPager,
}

notifyService := service.NewEventsService(logger, jobRepo, tenantService, notifierChannels, nil, nil, nil)
notifyService := service.NewEventsService(logger, jobRepo, tenantService, notifierChannels, nil, nil, nil, nil)

err := notifyService.Push(ctx, event)
assert.Nil(t, err)
Expand Down Expand Up @@ -306,7 +355,7 @@ func TestNotificationService(t *testing.T) {
"pagerduty": notifyChanelPager,
}

notifyService := service.NewEventsService(logger, jobRepo, tenantService, notifierChannels, nil, nil, nil)
notifyService := service.NewEventsService(logger, jobRepo, tenantService, notifierChannels, nil, nil, nil, nil)

err := notifyService.Push(ctx, event)

Expand All @@ -316,6 +365,22 @@ func TestNotificationService(t *testing.T) {
})
}

// todo: this is added as Lark Notifer
type mockLarkNotificationChanel struct {
io.Closer
mock.Mock
}

func (m *mockLarkNotificationChanel) Notify(ctx context.Context, attr scheduler.LarkNotifyAttrs) error {
args := m.Called(ctx, attr)
return args.Error(0)
}

func (m *mockLarkNotificationChanel) Close() error {
args := m.Called()
return args.Error(0)
}

type mockNotificationChanel struct {
io.Closer
mock.Mock
Expand Down
9 changes: 6 additions & 3 deletions core/tenant/secret.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,12 @@ import (
const (
EntitySecret = "secret"

SecretStorageKey = "STORAGE"
SecretSchedulerAuth = "SCHEDULER_AUTH"
SecretNotifySlack = "NOTIFY_SLACK"
SecretStorageKey = "STORAGE"
SecretSchedulerAuth = "SCHEDULER_AUTH"
SecretNotifySlack = "NOTIFY_SLACK"
SecretLarkAppID = "NOTIFY_LARK_APP_ID"
SecretLarkAppSecret = "NOTIFY_LARK_APP_SECRET"
SecretLarkVerificationToken = "NOTIFY_LARK_VERIFICATION_TOKEN"
)

type SecretName string
Expand Down
Loading
Loading