From 798c3dcc84d1d87bd9a7171d020a5a2cfd769394 Mon Sep 17 00:00:00 2001 From: Igor Komlew Date: Thu, 21 Dec 2017 14:20:50 +0100 Subject: [PATCH 1/3] list approvals, deployments, show help message for hipchat --- bot/{slack => }/deployments.go | 13 ++-- bot/hipchat/approvals.go | 112 +++++++-------------------------- bot/hipchat/hipchat.go | 52 ++++++++------- bot/slack/slack.go | 2 +- 4 files changed, 57 insertions(+), 122 deletions(-) rename bot/{slack => }/deployments.go (84%) diff --git a/bot/slack/deployments.go b/bot/deployments.go similarity index 84% rename from bot/slack/deployments.go rename to bot/deployments.go index 5f8e544c..b262dffe 100644 --- a/bot/slack/deployments.go +++ b/bot/deployments.go @@ -1,10 +1,11 @@ -package slack +package bot import ( "bytes" "fmt" "github.com/keel-hq/keel/bot/formatter" + "github.com/keel-hq/keel/provider/kubernetes" "k8s.io/client-go/pkg/apis/extensions/v1beta1" @@ -18,16 +19,16 @@ type Filter struct { } // deployments - gets all deployments -func (b *Bot) deployments() ([]v1beta1.Deployment, error) { +func deployments(k8sImplementer kubernetes.Implementer) ([]v1beta1.Deployment, error) { deploymentLists := []*v1beta1.DeploymentList{} - n, err := b.k8sImplementer.Namespaces() + n, err := k8sImplementer.Namespaces() if err != nil { return nil, err } for _, n := range n.Items { - l, err := b.k8sImplementer.Deployments(n.GetName()) + l, err := k8sImplementer.Deployments(n.GetName()) if err != nil { log.WithFields(log.Fields{ "error": err, @@ -49,8 +50,8 @@ func (b *Bot) deployments() ([]v1beta1.Deployment, error) { return impacted, nil } -func (b *Bot) deploymentsResponse(filter Filter) string { - deps, err := b.deployments() +func DeploymentsResponse(filter Filter, k8sImplementer kubernetes.Implementer) string { + deps, err := deployments(k8sImplementer) if err != nil { return fmt.Sprintf("got error while fetching deployments: %s", err) } diff --git a/bot/hipchat/approvals.go b/bot/hipchat/approvals.go index 9e69c864..f8e86971 100644 --- a/bot/hipchat/approvals.go +++ b/bot/hipchat/approvals.go @@ -13,11 +13,9 @@ import ( ) func (b *Bot) subscribeForApprovals() error { - log.Debugf(">>> hipchat.subscribeForApprovals()\n") - approvalsCh, err := b.approvalsManager.Subscribe(b.ctx) if err != nil { - log.Debugf(">>> [ERROR] hipchat.subscribeForApprovals(): %s\n", err.Error()) + log.Errorf("hipchat.subscribeForApprovals(): %s", err.Error()) return err } @@ -75,7 +73,6 @@ func (b *Bot) processApprovalResponses() error { }).Error("bot.processApprovalResponses: failed to process approval reject response message") } } - } } } @@ -107,7 +104,6 @@ func (b *Bot) processApprovedResponse(approvalResponse *bot.ApprovalResponse) er "identifier": identifier, }).Error("bot.processApprovedResponse: got error while replying after processing approved approval") } - } return nil } @@ -144,91 +140,31 @@ func (b *Bot) processRejectedResponse(approvalResponse *bot.ApprovalResponse) er func (b *Bot) replyToApproval(approval *types.Approval) error { switch approval.Status() { case types.ApprovalStatusPending: - b.postMessage("Vote received") - // "Vote received", - // "All approvals received, thanks for voting!", - // types.LevelInfo.Color(), - // []slack.AttachmentField{ - // slack.AttachmentField{ - // Title: "vote received!", - // Value: "Waiting for remaining votes.", - // Short: false, - // }, - // slack.AttachmentField{ - // Title: "Votes", - // Value: fmt.Sprintf("%d/%d", approval.VotesReceived, approval.VotesRequired), - // Short: true, - // }, - // slack.AttachmentField{ - // Title: "Delta", - // Value: approval.Delta(), - // Short: true, - // }, - // slack.AttachmentField{ - // Title: "Identifier", - // Value: approval.Identifier, - // Short: true, - // }, - // }) + msg := fmt.Sprintf(`Vote received + Waiting for remaining votes! + Votes: %d/%d + Delta: %s + Identifier: %s`, + approval.VotesReceived, approval.VotesRequired, approval.Delta(), approval.Identifier) + b.postMessage(msg) case types.ApprovalStatusRejected: - b.postMessage("Change rejected") - // "Change rejected", - // "Change was rejected", - // types.LevelWarn.Color(), - // []slack.AttachmentField{ - // slack.AttachmentField{ - // Title: "change rejected", - // Value: "Change was rejected.", - // Short: false, - // }, - // slack.AttachmentField{ - // Title: "Status", - // Value: approval.Status().String(), - // Short: true, - // }, - // slack.AttachmentField{ - // Title: "Votes", - // Value: fmt.Sprintf("%d/%d", approval.VotesReceived, approval.VotesRequired), - // Short: true, - // }, - // slack.AttachmentField{ - // Title: "Delta", - // Value: approval.Delta(), - // Short: true, - // }, - // slack.AttachmentField{ - // Title: "Identifier", - // Value: approval.Identifier, - // Short: true, - // }, - // }) + msg := fmt.Sprintf(`change rejected + Change was rejected. + Status: %s + Votes: %d/%d + Delta: %s + Identifier: %s`, + approval.Status().String(), approval.VotesReceived, approval.VotesRequired, + approval.Delta(), approval.Identifier) + b.postMessage(msg) case types.ApprovalStatusApproved: - b.postMessage("approval received") - // "approval received", - // "All approvals received, thanks for voting!", - // types.LevelSuccess.Color(), - // []slack.AttachmentField{ - // slack.AttachmentField{ - // Title: "update approved!", - // Value: "All approvals received, thanks for voting!", - // Short: false, - // }, - // slack.AttachmentField{ - // Title: "Votes", - // Value: fmt.Sprintf("%d/%d", approval.VotesReceived, approval.VotesRequired), - // Short: true, - // }, - // slack.AttachmentField{ - // Title: "Delta", - // Value: approval.Delta(), - // Short: true, - // }, - // slack.AttachmentField{ - // Title: "Identifier", - // Value: approval.Identifier, - // Short: true, - // }, - // }) + msg := fmt.Sprintf(`Update approved! + All approvals received, thanks for voting! + Votes: %d/%d + Delta: %s + Identifier: %s`, + approval.VotesReceived, approval.VotesRequired, approval.Delta(), approval.Identifier) + b.postMessage(msg) } return nil } diff --git a/bot/hipchat/hipchat.go b/bot/hipchat/hipchat.go index a8ef30f2..c8ba4d9e 100644 --- a/bot/hipchat/hipchat.go +++ b/bot/hipchat/hipchat.go @@ -3,7 +3,6 @@ package hipchat import ( "context" "errors" - "fmt" "os" "regexp" "strings" @@ -50,14 +49,16 @@ func init() { // Run ... func Run(k8sImplementer kubernetes.Implementer, approvalsManager approvals.Manager) (teardown func(), err error) { - if os.Getenv(constants.EnvHipchatApprovalsPasswort) != "" { + + if os.Getenv(constants.EnvHipchatApprovalsPasswort) != "" && + os.Getenv(constants.EnvHipchatApprovalsUserName) != "" { botName := "keel" if os.Getenv(constants.EnvHipchatApprovalsBotName) != "" { botName = os.Getenv(constants.EnvHipchatApprovalsBotName) } botUserName := "" - if os.Getenv(constants.EnvHipchatApprovalsUserName) != "" { // need this!!!! + if os.Getenv(constants.EnvHipchatApprovalsUserName) != "" { botUserName = os.Getenv(constants.EnvHipchatApprovalsUserName) } @@ -92,15 +93,13 @@ func Run(k8sImplementer kubernetes.Implementer, approvalsManager approvals.Manag //--------------------- ------------------------------------- func connect(username, password string) *hipchat.Client { - log.Debugf(">>> bot.hipchat.NewClient(): user=%s, pass=%s\n", username, password) - attempts := 10 for { - log.Debugf(">>> try to connect to hipchat") + log.Debug("try to connect to hipchat") client, err := hipchat.NewClient(username, password, "bot", "plain") // could not authenticate if err != nil { - log.Errorf("bot.hipchat.connect: Error=%s\n", err) + log.Errorf("bot.hipchat.connect: Error=%s", err) if err.Error() == "could not authenticate" { return nil } @@ -111,7 +110,7 @@ func connect(username, password string) *hipchat.Client { if client != nil && err == nil { return client } - log.Debugf("wait fo 30 seconds") + log.Debugln("wait fo 30 seconds") time.Sleep(30 * time.Second) attempts-- } @@ -177,25 +176,14 @@ func (b *Bot) startInternal() error { return nil } -// // A Message represents a message received from HipChat. -// type Message struct { -// From string -// To string -// Body string -// MentionName string -// } -// Body:"@IgorKomlew release notification from keel" -// hipchat.handleMessage(): &hipchat.Message{From:"701032_keel-bot@conf.hipchat.com", To:"701032_4966430@chat.hipchat.com/bot", Body:"release notification from keel", MentionName:""} func (b *Bot) handleMessage(message *hipchat.Message) { msg := b.trimXMPPMessage(message) - log.Debugf("hipchat.handleMessage(): %#v // %#v\n", message, msg) if msg.From == "" || msg.To == "" { - log.Debugf("hipchat.handleMessage(): ignore") return } if !b.isBotMessage(msg) { - log.Debugf("hipchat.handleMessage(): is not a bot message") + log.Debugln("hipchat.handleMessage(): is not a bot message") return } @@ -207,8 +195,7 @@ func (b *Bot) handleMessage(message *hipchat.Message) { if responseLines, ok := bot.BotEventTextToResponse[msg.Body]; ok { response := strings.Join(responseLines, "\n") - fmt.Println(">>> " + response) - b.respond(response) + b.respond(formatAsSnippet(response)) return } @@ -230,11 +217,25 @@ func (b *Bot) respond(response string) { } func (b *Bot) handleCommand(message *hipchat.Message) { - fmt.Printf("bot.hipchat.handleCommand() %v\n", message) + switch message.Body { + case "get deployments": + log.Info("getting deployments") + response := bot.DeploymentsResponse(bot.Filter{}, b.k8sImplementer) + b.respond(formatAsSnippet(response)) + return + case "get approvals": + log.Info("getting approvals") + response := b.approvalsResponse() + b.respond(formatAsSnippet(response)) + return + } +} + +func formatAsSnippet(msg string) string { + return "/code " + msg } func (b *Bot) isCommand(message *hipchat.Message) bool { - fmt.Printf("bot.hipchat.isCommand=%s\n", message.Body) if bot.StaticBotCommands[message.Body] { return true @@ -284,14 +285,11 @@ func (b *Bot) trimUser(user string) string { } func (b *Bot) postMessage(msg string) error { - log.Debugf(">>> bot.hipchat.postMessage: %s\n", msg) b.hipchatClient.Say(b.approvalsChannel, b.name, msg) - // b.respond(msg) return nil } func (b *Bot) trimBot(msg string) string { - // msg = strings.Replace(msg, strings.ToLower(b.msgPrefix), "", 1) msg = strings.TrimPrefix(msg, b.mentionName) msg = strings.Trim(msg, "\n") msg = strings.TrimSpace(msg) diff --git a/bot/slack/slack.go b/bot/slack/slack.go index 511e9b38..dd8d6afe 100644 --- a/bot/slack/slack.go +++ b/bot/slack/slack.go @@ -312,7 +312,7 @@ func (b *Bot) handleCommand(event *slack.MessageEvent, eventText string) { switch eventText { case "get deployments": log.Info("getting deployments") - response := b.deploymentsResponse(Filter{}) + response := bot.DeploymentsResponse(bot.Filter{}, b.k8sImplementer) b.respond(event, formatAsSnippet(response)) return case "get approvals": From cadfdf2bbc84388db7154b30c603ddf6075e20bb Mon Sep 17 00:00:00 2001 From: Igor Komlew Date: Thu, 21 Dec 2017 18:17:04 +0100 Subject: [PATCH 2/3] move some duplicated approvals code to the base bot package --- bot/approvals.go | 42 ++++++++++++++++++++ bot/bot.go | 3 +- bot/hipchat/approvals.go | 84 ++++++++++++---------------------------- bot/hipchat/hipchat.go | 58 ++++++++++++++++++++------- bot/slack/approvals.go | 35 ----------------- bot/slack/slack.go | 6 ++- 6 files changed, 117 insertions(+), 111 deletions(-) create mode 100644 bot/approvals.go diff --git a/bot/approvals.go b/bot/approvals.go new file mode 100644 index 00000000..bc590b64 --- /dev/null +++ b/bot/approvals.go @@ -0,0 +1,42 @@ +package bot + +import ( + "bytes" + "fmt" + + "github.com/keel-hq/keel/approvals" + "github.com/keel-hq/keel/bot/formatter" +) + +func ApprovalsResponse(approvalsManager approvals.Manager) string { + approvals, err := approvalsManager.List() + if err != nil { + return fmt.Sprintf("got error while fetching approvals: %s", err) + } + + if len(approvals) == 0 { + return fmt.Sprintf("there are currently no request waiting to be approved.") + } + + buf := &bytes.Buffer{} + + approvalCtx := formatter.Context{ + Output: buf, + Format: formatter.NewApprovalsFormat(formatter.TableFormatKey, false), + } + err = formatter.ApprovalWrite(approvalCtx, approvals) + + if err != nil { + return fmt.Sprintf("got error while formatting approvals: %s", err) + } + + return buf.String() +} + +func RemoveApprovalHandler(identifier string, approvalsManager approvals.Manager) string { + err := approvalsManager.Delete(identifier) + if err != nil { + return fmt.Sprintf("failed to remove '%s' approval: %s.", identifier, err) + } + return fmt.Sprintf("approval '%s' removed.", identifier) +} diff --git a/bot/bot.go b/bot/bot.go index 9d940ebc..6e1a8533 100644 --- a/bot/bot.go +++ b/bot/bot.go @@ -93,7 +93,6 @@ func Run(k8sImplementer kubernetes.Implementer, approvalsManager approvals.Manag "error": err, }).Fatalf("main: failed to setup %s bot\n", botName) } else { - log.Debugf(">>> Run [%s] bot", botName) teardowns[botName] = teardownBot } } @@ -101,7 +100,7 @@ func Run(k8sImplementer kubernetes.Implementer, approvalsManager approvals.Manag func Stop() { for botName, teardown := range teardowns { - log.Infof("Teardown %s bot\n", botName) + log.Infof("Teardown %s bot", botName) teardown() } } diff --git a/bot/hipchat/approvals.go b/bot/hipchat/approvals.go index f8e86971..49b85e6c 100644 --- a/bot/hipchat/approvals.go +++ b/bot/hipchat/approvals.go @@ -1,12 +1,10 @@ package hipchat import ( - "bytes" "fmt" "strings" "github.com/keel-hq/keel/bot" - "github.com/keel-hq/keel/bot/formatter" "github.com/keel-hq/keel/types" log "github.com/Sirupsen/logrus" @@ -37,14 +35,15 @@ func (b *Bot) subscribeForApprovals() error { // Request - request approval func (b *Bot) requestApproval(req *types.Approval) error { - msg := fmt.Sprintf(`Approval required! - %s - To vote for change type '%s approve %s' - To reject it: '%s reject %s' - Votes: %d/%d - Delta: %s - Identifier: %s - Provider: %s`, + tml := `Approval required! + %s + To vote for change type '%s approve %s' + To reject it: '%s reject %s' + Votes: %d/%d + Delta: %s + Identifier: %s + Provider: %s` + msg := fmt.Sprintf(tml, req.Message, b.mentionName, req.Identifier, b.mentionName, req.Identifier, req.VotesReceived, req.VotesRequired, req.Delta(), req.Identifier, req.Provider.String()) @@ -141,63 +140,30 @@ func (b *Bot) replyToApproval(approval *types.Approval) error { switch approval.Status() { case types.ApprovalStatusPending: msg := fmt.Sprintf(`Vote received - Waiting for remaining votes! - Votes: %d/%d - Delta: %s - Identifier: %s`, + Waiting for remaining votes! + Votes: %d/%d + Delta: %s + Identifier: %s`, approval.VotesReceived, approval.VotesRequired, approval.Delta(), approval.Identifier) - b.postMessage(msg) + b.postMessage(formatAsSnippet(msg)) case types.ApprovalStatusRejected: msg := fmt.Sprintf(`change rejected - Change was rejected. - Status: %s - Votes: %d/%d - Delta: %s - Identifier: %s`, + Change was rejected. + Status: %s + Votes: %d/%d + Delta: %s + Identifier: %s`, approval.Status().String(), approval.VotesReceived, approval.VotesRequired, approval.Delta(), approval.Identifier) - b.postMessage(msg) + b.postMessage(formatAsSnippet(msg)) case types.ApprovalStatusApproved: msg := fmt.Sprintf(`Update approved! - All approvals received, thanks for voting! - Votes: %d/%d - Delta: %s - Identifier: %s`, + All approvals received, thanks for voting! + Votes: %d/%d + Delta: %s + Identifier: %s`, approval.VotesReceived, approval.VotesRequired, approval.Delta(), approval.Identifier) - b.postMessage(msg) + b.postMessage(formatAsSnippet(msg)) } return nil } - -func (b *Bot) approvalsResponse() string { - approvals, err := b.approvalsManager.List() - if err != nil { - return fmt.Sprintf("got error while fetching approvals: %s", err) - } - - if len(approvals) == 0 { - return fmt.Sprintf("there are currently no request waiting to be approved.") - } - - buf := &bytes.Buffer{} - - approvalCtx := formatter.Context{ - Output: buf, - Format: formatter.NewApprovalsFormat(formatter.TableFormatKey, false), - } - err = formatter.ApprovalWrite(approvalCtx, approvals) - - if err != nil { - return fmt.Sprintf("got error while formatting approvals: %s", err) - } - - return buf.String() -} - -func (b *Bot) removeApprovalHandler(identifier string) string { - err := b.approvalsManager.Delete(identifier) - if err != nil { - return fmt.Sprintf("failed to remove '%s' approval: %s.", identifier, err) - } - return fmt.Sprintf("approval '%s' removed.", identifier) -} diff --git a/bot/hipchat/hipchat.go b/bot/hipchat/hipchat.go index c8ba4d9e..7c4ff7a6 100644 --- a/bot/hipchat/hipchat.go +++ b/bot/hipchat/hipchat.go @@ -86,7 +86,7 @@ func Run(k8sImplementer kubernetes.Implementer, approvalsManager approvals.Manag return teardown, nil } - + log.Info("bot.hipchat.Run(): HipChat approval bot ist not configured, ignore") return func() {}, nil } @@ -108,6 +108,7 @@ func connect(username, password string) *hipchat.Client { return nil } if client != nil && err == nil { + log.Info("Successfully connected to hipchat server") return client } log.Debugln("wait fo 30 seconds") @@ -137,7 +138,6 @@ func new(name, username, pass, approvalsChannel string, k8sImplementer kubernete // Start the bot func (b *Bot) Start(ctx context.Context) error { - log.Debugln(">>> bot.hipchat.Start()") if b.hipchatClient == nil { return errors.New("could not conect to hipchat server") @@ -159,16 +159,28 @@ func (b *Bot) Start(ctx context.Context) error { } func (b *Bot) startInternal() error { + log.Debug("bot.hipchat.startInternal()") client := b.hipchatClient - log.Debugf("startInternal(): channel=%s, userName=%s\n", b.approvalsChannel, b.name) - client.Status("chat") // chat, away or idle + client.Status("chat") client.Join(b.approvalsChannel, b.name) + b.postMessage("Keel bot started ...") go client.KeepAlive() go func() { + log.Debug("Starting hipchat main process loop") for { select { + case users := <-client.Users(): + log.Debugf("hipchat.Users: %#v", users) + + case rooms := <-client.Rooms(): + log.Debugf("hipchat.Rooms: %#v", rooms) + case message := <-client.Messages(): + log.Debug("hipchat.Messages: Incomming message") b.handleMessage(message) + + default: + continue } } }() @@ -178,28 +190,33 @@ func (b *Bot) startInternal() error { func (b *Bot) handleMessage(message *hipchat.Message) { msg := b.trimXMPPMessage(message) + log.Debugf("hipchat.handleMessage(): %#v", message) if msg.From == "" || msg.To == "" { + log.Debugln("hipchat.handleMessage(): fields 'From:' or 'To:' are empty, ignore") return } if !b.isBotMessage(msg) { - log.Debugln("hipchat.handleMessage(): is not a bot message") + log.Debugf("hipchat.handleMessage(): [%s] is not a bot message", msg) return } approval, ok := b.isApproval(msg) if ok { + log.Debugf("hipchat.handleMessage(): [%s] is approval command", msg) b.approvalsRespCh <- approval return } if responseLines, ok := bot.BotEventTextToResponse[msg.Body]; ok { + log.Debugf("hipchat.handleMessage(): [%s] is a help command", msg) response := strings.Join(responseLines, "\n") b.respond(formatAsSnippet(response)) return } if b.isCommand(msg) { + log.Debugf("hipchat.handleMessage(): [%s] is a command", msg) b.handleCommand(msg) return } @@ -212,12 +229,10 @@ func (b *Bot) handleMessage(message *hipchat.Message) { }).Debug("handleMessage: bot couldn't recognise command") } -func (b *Bot) respond(response string) { - b.hipchatClient.Say(b.approvalsChannel, b.name, response) -} - func (b *Bot) handleCommand(message *hipchat.Message) { - switch message.Body { + eventText := message.Body + log.Debugf("hipchat.handleCommand(): %#v", message) + switch eventText { case "get deployments": log.Info("getting deployments") response := bot.DeploymentsResponse(bot.Filter{}, b.k8sImplementer) @@ -225,10 +240,20 @@ func (b *Bot) handleCommand(message *hipchat.Message) { return case "get approvals": log.Info("getting approvals") - response := b.approvalsResponse() + response := bot.ApprovalsResponse(b.approvalsManager) b.respond(formatAsSnippet(response)) return } + + // handle dynamic commands + if strings.HasPrefix(eventText, bot.RemoveApprovalPrefix) { + id := strings.TrimSpace(strings.TrimPrefix(eventText, bot.RemoveApprovalPrefix)) + snippet := bot.RemoveApprovalHandler(id, b.approvalsManager) + b.respond(formatAsSnippet(snippet)) + return + } + + log.Info("hipchat.handleCommand(): command not found") } func formatAsSnippet(msg string) string { @@ -262,12 +287,12 @@ func (b *Bot) trimXMPPMessage(message *hipchat.Message) *hipchat.Message { func trimMentionName(message string) string { re := regexp.MustCompile(`^(@\w+)`) - match := re.FindStringSubmatch(message) + match := re.FindStringSubmatch(strings.TrimSpace(message)) if match == nil { return "" } if len(match) != 0 { - return match[1] + return strings.TrimSpace(match[1]) } return "" } @@ -289,6 +314,10 @@ func (b *Bot) postMessage(msg string) error { return nil } +func (b *Bot) respond(response string) { + b.hipchatClient.Say(b.approvalsChannel, b.name, response) +} + func (b *Bot) trimBot(msg string) string { msg = strings.TrimPrefix(msg, b.mentionName) msg = strings.Trim(msg, "\n") @@ -318,8 +347,11 @@ func (b *Bot) isApproval(message *hipchat.Message) (resp *bot.ApprovalResponse, } func (b *Bot) isBotMessage(message *hipchat.Message) bool { + log.Debugf("bot.hipchat.isBotMessage(): %#v", message) if message.MentionName == b.mentionName { return true } + log.Debugf("bot.hipchat.isBotMessage(): bot MentionName is not correct: [%s] != [%s]", + message.MentionName, b.mentionName) return false } diff --git a/bot/slack/approvals.go b/bot/slack/approvals.go index f78cb49f..6a96c4ec 100644 --- a/bot/slack/approvals.go +++ b/bot/slack/approvals.go @@ -1,12 +1,10 @@ package slack import ( - "bytes" "fmt" "strings" "github.com/keel-hq/keel/bot" - "github.com/keel-hq/keel/bot/formatter" "github.com/keel-hq/keel/types" "github.com/nlopes/slack" @@ -252,36 +250,3 @@ func (b *Bot) replyToApproval(approval *types.Approval) error { } return nil } - -func (b *Bot) approvalsResponse() string { - approvals, err := b.approvalsManager.List() - if err != nil { - return fmt.Sprintf("got error while fetching approvals: %s", err) - } - - if len(approvals) == 0 { - return fmt.Sprintf("there are currently no request waiting to be approved.") - } - - buf := &bytes.Buffer{} - - approvalCtx := formatter.Context{ - Output: buf, - Format: formatter.NewApprovalsFormat(formatter.TableFormatKey, false), - } - err = formatter.ApprovalWrite(approvalCtx, approvals) - - if err != nil { - return fmt.Sprintf("got error while formatting approvals: %s", err) - } - - return buf.String() -} - -func (b *Bot) removeApprovalHandler(identifier string) string { - err := b.approvalsManager.Delete(identifier) - if err != nil { - return fmt.Sprintf("failed to remove '%s' approval: %s.", identifier, err) - } - return fmt.Sprintf("approval '%s' removed.", identifier) -} diff --git a/bot/slack/slack.go b/bot/slack/slack.go index dd8d6afe..0010da7d 100644 --- a/bot/slack/slack.go +++ b/bot/slack/slack.go @@ -316,14 +316,16 @@ func (b *Bot) handleCommand(event *slack.MessageEvent, eventText string) { b.respond(event, formatAsSnippet(response)) return case "get approvals": - response := b.approvalsResponse() + response := bot.ApprovalsResponse(b.approvalsManager) b.respond(event, formatAsSnippet(response)) return } // handle dynamic commands if strings.HasPrefix(eventText, bot.RemoveApprovalPrefix) { - b.respond(event, formatAsSnippet(b.removeApprovalHandler(strings.TrimSpace(strings.TrimPrefix(eventText, bot.RemoveApprovalPrefix))))) + id := strings.TrimSpace(strings.TrimPrefix(eventText, bot.RemoveApprovalPrefix)) + snippet := bot.RemoveApprovalHandler(id, b.approvalsManager) + b.respond(event, formatAsSnippet(snippet)) return } From 84859f919d10ee17babf6b5839b16437f6fefc29 Mon Sep 17 00:00:00 2001 From: Igor Komlew Date: Fri, 22 Dec 2017 12:40:44 +0100 Subject: [PATCH 3/3] Move responses for hip chat bot to separate file --- bot/hipchat/approvals.go | 32 +++++--------------------------- bot/hipchat/hipchat.go | 25 ++----------------------- bot/hipchat/templates.go | 29 +++++++++++++++++++++++++++++ 3 files changed, 36 insertions(+), 50 deletions(-) create mode 100644 bot/hipchat/templates.go diff --git a/bot/hipchat/approvals.go b/bot/hipchat/approvals.go index 49b85e6c..95ecc951 100644 --- a/bot/hipchat/approvals.go +++ b/bot/hipchat/approvals.go @@ -35,19 +35,11 @@ func (b *Bot) subscribeForApprovals() error { // Request - request approval func (b *Bot) requestApproval(req *types.Approval) error { - tml := `Approval required! - %s - To vote for change type '%s approve %s' - To reject it: '%s reject %s' - Votes: %d/%d - Delta: %s - Identifier: %s - Provider: %s` - msg := fmt.Sprintf(tml, + msg := fmt.Sprintf(ApprovalRequiredTempl, req.Message, b.mentionName, req.Identifier, b.mentionName, req.Identifier, req.VotesReceived, req.VotesRequired, req.Delta(), req.Identifier, req.Provider.String()) - return b.postMessage(msg) + return b.postMessage(formatAsSnippet(msg)) } func (b *Bot) processApprovalResponses() error { @@ -131,7 +123,6 @@ func (b *Bot) processRejectedResponse(approvalResponse *bot.ApprovalResponse) er "identifier": identifier, }).Error("bot.processApprovedResponse: got error while replying after processing rejected approval") } - } return nil } @@ -139,29 +130,16 @@ func (b *Bot) processRejectedResponse(approvalResponse *bot.ApprovalResponse) er func (b *Bot) replyToApproval(approval *types.Approval) error { switch approval.Status() { case types.ApprovalStatusPending: - msg := fmt.Sprintf(`Vote received - Waiting for remaining votes! - Votes: %d/%d - Delta: %s - Identifier: %s`, + msg := fmt.Sprintf(VoteReceivedTempl, approval.VotesReceived, approval.VotesRequired, approval.Delta(), approval.Identifier) b.postMessage(formatAsSnippet(msg)) case types.ApprovalStatusRejected: - msg := fmt.Sprintf(`change rejected - Change was rejected. - Status: %s - Votes: %d/%d - Delta: %s - Identifier: %s`, + msg := fmt.Sprintf(ChangeRejectedTempl, approval.Status().String(), approval.VotesReceived, approval.VotesRequired, approval.Delta(), approval.Identifier) b.postMessage(formatAsSnippet(msg)) case types.ApprovalStatusApproved: - msg := fmt.Sprintf(`Update approved! - All approvals received, thanks for voting! - Votes: %d/%d - Delta: %s - Identifier: %s`, + msg := fmt.Sprintf(UpdateApprovedTempl, approval.VotesReceived, approval.VotesRequired, approval.Delta(), approval.Identifier) b.postMessage(formatAsSnippet(msg)) } diff --git a/bot/hipchat/hipchat.go b/bot/hipchat/hipchat.go index 7c4ff7a6..f0626ee0 100644 --- a/bot/hipchat/hipchat.go +++ b/bot/hipchat/hipchat.go @@ -95,7 +95,7 @@ func Run(k8sImplementer kubernetes.Implementer, approvalsManager approvals.Manag func connect(username, password string) *hipchat.Client { attempts := 10 for { - log.Debug("try to connect to hipchat") + log.Info("bot.hipchat.connect: try to connect to hipchat") client, err := hipchat.NewClient(username, password, "bot", "plain") // could not authenticate if err != nil { @@ -111,7 +111,7 @@ func connect(username, password string) *hipchat.Client { log.Info("Successfully connected to hipchat server") return client } - log.Debugln("wait fo 30 seconds") + log.Debugln("Can not connect to hipcaht now, wait fo 30 seconds") time.Sleep(30 * time.Second) attempts-- } @@ -159,28 +159,16 @@ func (b *Bot) Start(ctx context.Context) error { } func (b *Bot) startInternal() error { - log.Debug("bot.hipchat.startInternal()") client := b.hipchatClient client.Status("chat") client.Join(b.approvalsChannel, b.name) b.postMessage("Keel bot started ...") go client.KeepAlive() go func() { - log.Debug("Starting hipchat main process loop") for { select { - case users := <-client.Users(): - log.Debugf("hipchat.Users: %#v", users) - - case rooms := <-client.Rooms(): - log.Debugf("hipchat.Rooms: %#v", rooms) - case message := <-client.Messages(): - log.Debug("hipchat.Messages: Incomming message") b.handleMessage(message) - - default: - continue } } }() @@ -190,33 +178,28 @@ func (b *Bot) startInternal() error { func (b *Bot) handleMessage(message *hipchat.Message) { msg := b.trimXMPPMessage(message) - log.Debugf("hipchat.handleMessage(): %#v", message) if msg.From == "" || msg.To == "" { log.Debugln("hipchat.handleMessage(): fields 'From:' or 'To:' are empty, ignore") return } if !b.isBotMessage(msg) { - log.Debugf("hipchat.handleMessage(): [%s] is not a bot message", msg) return } approval, ok := b.isApproval(msg) if ok { - log.Debugf("hipchat.handleMessage(): [%s] is approval command", msg) b.approvalsRespCh <- approval return } if responseLines, ok := bot.BotEventTextToResponse[msg.Body]; ok { - log.Debugf("hipchat.handleMessage(): [%s] is a help command", msg) response := strings.Join(responseLines, "\n") b.respond(formatAsSnippet(response)) return } if b.isCommand(msg) { - log.Debugf("hipchat.handleMessage(): [%s] is a command", msg) b.handleCommand(msg) return } @@ -231,7 +214,6 @@ func (b *Bot) handleMessage(message *hipchat.Message) { func (b *Bot) handleCommand(message *hipchat.Message) { eventText := message.Body - log.Debugf("hipchat.handleCommand(): %#v", message) switch eventText { case "get deployments": log.Info("getting deployments") @@ -347,11 +329,8 @@ func (b *Bot) isApproval(message *hipchat.Message) (resp *bot.ApprovalResponse, } func (b *Bot) isBotMessage(message *hipchat.Message) bool { - log.Debugf("bot.hipchat.isBotMessage(): %#v", message) if message.MentionName == b.mentionName { return true } - log.Debugf("bot.hipchat.isBotMessage(): bot MentionName is not correct: [%s] != [%s]", - message.MentionName, b.mentionName) return false } diff --git a/bot/hipchat/templates.go b/bot/hipchat/templates.go new file mode 100644 index 00000000..feb4699e --- /dev/null +++ b/bot/hipchat/templates.go @@ -0,0 +1,29 @@ +package hipchat + +var ApprovalRequiredTempl = `Approval required! + %s + To vote for change type '%s approve %s' + To reject it: '%s reject %s' + Votes: %d/%d + Delta: %s + Identifier: %s + Provider: %s` + +var VoteReceivedTempl = `Vote received + Waiting for remaining votes! + Votes: %d/%d + Delta: %s + Identifier: %s` + +var ChangeRejectedTempl = `Change rejected + Change was rejected. + Status: %s + Votes: %d/%d + Delta: %s + Identifier: %s` + +var UpdateApprovedTempl = `Update approved! + All approvals received, thanks for voting! + Votes: %d/%d + Delta: %s + Identifier: %s`