forked from knative/pkg
-
Notifications
You must be signed in to change notification settings - Fork 0
/
message.go
129 lines (113 loc) · 3.72 KB
/
message.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
/*
Copyright 2019 The Knative Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package slack
import (
"flag"
"fmt"
"strings"
"sync"
"time"
"knative.dev/pkg/test/helpers"
"knative.dev/pkg/test/mako/config"
"knative.dev/pkg/test/slackutil"
)
var minInterval = flag.Duration("min-alert-interval", 24*time.Hour, "The minimum interval of sending Slack alerts.")
const (
messageTemplate = `
As of %s, there is a new performance regression detected from test automation for %s:
%s`
)
// MessageHandler handles methods for slack messages
type MessageHandler struct {
readClient slackutil.ReadOperations
writeClient slackutil.WriteOperations
channels []config.Channel
dryrun bool
}
// Setup creates the necessary setup to make calls to work with slack
func Setup(userName, readTokenPath, writeTokenPath string, channels []config.Channel, dryrun bool) (*MessageHandler, error) {
readClient, err := slackutil.NewReadClient(userName, readTokenPath)
if err != nil {
return nil, fmt.Errorf("cannot authenticate to slack read client: %v", err)
}
writeClient, err := slackutil.NewWriteClient(userName, writeTokenPath)
if err != nil {
return nil, fmt.Errorf("cannot authenticate to slack write client: %v", err)
}
return &MessageHandler{
readClient: readClient,
writeClient: writeClient,
channels: channels,
dryrun: dryrun,
}, nil
}
// SendAlert will send alert for performance regression to the slack channel(s)
func (smh *MessageHandler) SendAlert(testName, summary string) error {
dryrun := smh.dryrun
errCh := make(chan error)
var wg sync.WaitGroup
for i := range smh.channels {
channel := smh.channels[i]
wg.Add(1)
go func() {
defer wg.Done()
// get the recent message history in the channel for this user
startTime := time.Now().Add(-1 * *minInterval)
var messageHistory []string
if err := helpers.Run(
fmt.Sprintf("retrieving message history in channel %q", channel.Name),
func() error {
var err error
messageHistory, err = smh.readClient.MessageHistory(channel.Identity, startTime)
return err
},
dryrun,
); err != nil {
errCh <- fmt.Errorf("failed to retrieve message history in channel %q", channel.Name)
}
// decorate the test name for more accurate match
decoratedTestName := decoratedName(testName)
// do not send message again if alert for this test has been sent to
// the channel a short while ago
for _, message := range messageHistory {
if strings.Contains(message, decoratedTestName) {
return
}
}
// send the alert message to the channel
message := fmt.Sprintf(messageTemplate, time.Now().UTC(), decoratedTestName, summary)
if err := helpers.Run(
fmt.Sprintf("sending message %q to channel %q", message, channel.Name),
func() error {
return smh.writeClient.Post(message, channel.Identity)
},
dryrun,
); err != nil {
errCh <- fmt.Errorf("failed to send message to channel %q", channel.Name)
}
}()
}
go func() {
wg.Wait()
close(errCh)
}()
errs := make([]error, 0)
for err := range errCh {
errs = append(errs, err)
}
return helpers.CombineErrors(errs)
}
// decoratedName returns a name with decoration for easy identification.
func decoratedName(name string) string {
return fmt.Sprintf("[%s]", name)
}