Skip to content

Commit e255f55

Browse files
committed
provider/slack: add new alert integration
1 parent 3fab255 commit e255f55

File tree

9 files changed

+370
-40
lines changed

9 files changed

+370
-40
lines changed

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ require (
99
github.com/google/uuid v1.6.0
1010
github.com/gorilla/mux v1.8.1
1111
github.com/moov-io/base v0.53.0
12+
github.com/slack-go/slack v0.15.0
1213
github.com/spf13/viper v1.19.0
1314
github.com/stretchr/testify v1.10.0
1415
)
@@ -19,6 +20,7 @@ require (
1920
github.com/go-kit/log v0.2.1 // indirect
2021
github.com/go-logfmt/logfmt v0.6.0 // indirect
2122
github.com/google/go-querystring v1.1.0 // indirect
23+
github.com/gorilla/websocket v1.4.2 // indirect
2224
github.com/hashicorp/hcl v1.0.0 // indirect
2325
github.com/magiconair/properties v1.8.7 // indirect
2426
github.com/mitchellh/mapstructure v1.5.0 // indirect

go.sum

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU=
1111
github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
1212
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
1313
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
14+
github.com/go-test/deep v1.0.4 h1:u2CU3YKy9I2pmu9pX0eq50wCgjfGIt539SqR7FbHiho=
15+
github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
1416
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
17+
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
1518
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
1619
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
1720
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
@@ -20,6 +23,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
2023
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
2124
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
2225
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
26+
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
27+
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
2328
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
2429
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
2530
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
@@ -34,6 +39,7 @@ github.com/moov-io/base v0.53.0 h1:rpPWEbd/NTWApLzFq2AYbCZUlIv99OtvQcan7yArJVE=
3439
github.com/moov-io/base v0.53.0/go.mod h1:F2cdACBgJHNemPrOxvc88ezIqFL6ymErB4hOuPR+axg=
3540
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
3641
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
42+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
3743
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
3844
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
3945
github.com/rickar/cal/v2 v2.1.19 h1:rhWU/LGZnBwsEiJXMRAM9JooLH/8OHN8wWZ9qq13XJk=
@@ -44,6 +50,8 @@ github.com/sagikazarmark/locafero v0.6.0 h1:ON7AQg37yzcRPU69mt7gwhFEBwxI6P9T4Qu3
4450
github.com/sagikazarmark/locafero v0.6.0/go.mod h1:77OmuIc6VTraTXKXIs/uvUxKGUXjE1GbemJYHqdNjX0=
4551
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
4652
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
53+
github.com/slack-go/slack v0.15.0 h1:LE2lj2y9vqqiOf+qIIy0GvEoxgF1N5yLGZffmEZykt0=
54+
github.com/slack-go/slack v0.15.0/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw=
4755
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
4856
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
4957
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
@@ -54,8 +62,7 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
5462
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
5563
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
5664
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
57-
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
58-
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
65+
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
5966
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
6067
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
6168
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=

internal/check/checks.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,5 +100,16 @@ func mergeAlertConfigs(local, global config.Alert) config.Alert {
100100
}
101101
}
102102

103+
// Slack config merging
104+
out.Slack = cmp.Or(local.Slack, global.Slack)
105+
106+
if local.Slack != nil && global.Slack != nil {
107+
// Prefer the local config over the global config
108+
out.Slack = &config.Slack{
109+
ApiToken: cmp.Or(local.Slack.ApiToken, global.Slack.ApiToken),
110+
ChannelID: cmp.Or(local.Slack.ChannelID, global.Slack.ChannelID),
111+
}
112+
}
113+
103114
return out
104115
}

internal/config/config.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ func Load(path string) (*Config, error) {
4343
if pd := ReadPagerDutyFromEnv(); pd != nil {
4444
cfg.Alert.PagerDuty = pd
4545
}
46+
if sk := ReadSlackFromEnv(); sk != nil {
47+
cfg.Alert.Slack = sk
48+
}
4649

4750
return &cfg, nil
4851
}
@@ -105,6 +108,7 @@ func (t PartialDay) GetTimes() ([]time.Time, error) {
105108

106109
type Alert struct {
107110
PagerDuty *PagerDuty `yaml:"pagerduty"`
111+
Slack *Slack
108112
}
109113

110114
type PagerDuty struct {
@@ -134,3 +138,22 @@ func ReadPagerDutyFromEnv() *PagerDuty {
134138
}
135139
return nil
136140
}
141+
142+
type Slack struct {
143+
ApiToken string
144+
ChannelID string
145+
}
146+
147+
func ReadSlackFromEnv() *Slack {
148+
apiToken := os.Getenv("DEADCHECK_SLACK_API_TOKEN")
149+
channelID := os.Getenv("DEADCHECK_SLACK_CHANNEL_ID")
150+
151+
if apiToken != "" && channelID != "" {
152+
return &Slack{
153+
ApiToken: apiToken,
154+
ChannelID: channelID,
155+
}
156+
}
157+
158+
return nil
159+
}

internal/config/tolerance.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package config
2+
3+
import (
4+
"fmt"
5+
"time"
6+
)
7+
8+
func GetTolerance(schedule ScheduleConfig) time.Duration {
9+
var input string
10+
if schedule.Weekdays != nil {
11+
input = schedule.Weekdays.Tolerance
12+
}
13+
if schedule.BankingDays != nil {
14+
input = schedule.BankingDays.Tolerance
15+
}
16+
17+
dur, _ := time.ParseDuration(input)
18+
return dur
19+
}
20+
21+
func WithinTolerance(now, scheduleTime time.Time, schedule ScheduleConfig) error {
22+
tolerance := GetTolerance(schedule)
23+
24+
if tolerance > time.Duration(0) {
25+
// Allow checkins before the scheduled check-in time according to the tolerance
26+
switch {
27+
case now.Before(scheduleTime):
28+
// We are early to check-in
29+
diff := scheduleTime.Sub(now)
30+
if diff > tolerance {
31+
return fmt.Errorf("%v check-in not allowed for %v", scheduleTime.Format("15:04"), diff)
32+
}
33+
case now.Equal(scheduleTime):
34+
// do nothing, we're on time
35+
return nil
36+
37+
case scheduleTime.Before(now):
38+
// We are late to check-in
39+
diff := now.Sub(scheduleTime)
40+
if diff > tolerance {
41+
return fmt.Errorf("%v check-in is late by %v", scheduleTime.Format("15:04"), diff-tolerance)
42+
}
43+
}
44+
}
45+
46+
return nil
47+
}

internal/provider/pd/client.go

Lines changed: 3 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -138,31 +138,9 @@ func (c *client) CheckIn(ctx context.Context, check config.Check) (time.Time, er
138138
// Only allow check-ins with the tolerance specified.
139139
// e.g. If the tolerance is 5mins for a check-in expected at 4pm then only between 3:55pm and 4:05pm
140140
// would check-ins be allowed.
141-
tolerance := getTolerance(check.Schedule)
142-
143-
if tolerance > time.Duration(0) {
144-
// Allow checkins before the scheduled check-in time according to the tolerance
145-
switch {
146-
case now.Before(scheduleTime):
147-
// We are early to check-in
148-
diff := scheduleTime.Sub(now)
149-
if diff > tolerance {
150-
err = fmt.Errorf("%v check-in not allowed for %v", scheduleTime.Format("15:04"), diff)
151-
logger.Warn().Log(err.Error())
152-
return time.Time{}, err
153-
}
154-
case now.Equal(scheduleTime):
155-
// do nothing, we're on time
156-
157-
case scheduleTime.Before(now):
158-
// We are late to check-in
159-
diff := now.Sub(scheduleTime)
160-
if diff > tolerance {
161-
err = fmt.Errorf("%v check-in is late by %v", scheduleTime.Format("15:04"), diff-tolerance)
162-
logger.Warn().Log(err.Error())
163-
return time.Time{}, err
164-
}
165-
}
141+
err = config.WithinTolerance(now, scheduleTime, check.Schedule)
142+
if err != nil {
143+
return time.Time{}, logger.Error().LogError(err).Err()
166144
}
167145

168146
// future := now.Add(wait)
@@ -181,16 +159,3 @@ func (c *client) CheckIn(ctx context.Context, check config.Check) (time.Time, er
181159

182160
return future, nil
183161
}
184-
185-
func getTolerance(schedule config.ScheduleConfig) time.Duration {
186-
var input string
187-
if schedule.Weekdays != nil {
188-
input = schedule.Weekdays.Tolerance
189-
}
190-
if schedule.BankingDays != nil {
191-
input = schedule.BankingDays.Tolerance
192-
}
193-
194-
dur, _ := time.ParseDuration(input)
195-
return dur
196-
}

internal/provider/provider.go

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

88
"github.com/adamdecaf/deadcheck/internal/config"
99
"github.com/adamdecaf/deadcheck/internal/provider/pd"
10+
"github.com/adamdecaf/deadcheck/internal/provider/slack"
1011

1112
"github.com/moov-io/base/log"
1213
"github.com/moov-io/base/stime"
@@ -23,6 +24,8 @@ func NewClient(logger log.Logger, conf config.Alert) (Client, error) {
2324
switch {
2425
case conf.PagerDuty != nil:
2526
return pd.NewClient(logger, conf.PagerDuty, timeService)
27+
case conf.Slack != nil:
28+
return slack.NewClient(logger, conf.Slack, timeService)
2629
}
2730
return nil, errors.New("no provider configured")
2831
}

0 commit comments

Comments
 (0)