Files
houston-be/internal/processor/action/set_severity_command_action.go
Vijay Joshi 2eba625b0d INFRA-2888 : Added custom metrics on all major flows (#393)
* INFRA-2888 : Added alerts on all major flows

* INFRA-2888 : Remove unnecessary space

* INFRA-2888 : Metric handler

* INFRA-2888 : Review changes

* INFRA-2888 : Build fix

* INFRA-2888 : Code cleanup

* INFRA-2888 : Review comments round 1

* INFRA-2888 : Err msg changes

* INFRA-2888 : task to job in name
2024-03-12 19:56:52 +05:30

196 lines
7.5 KiB
Go

package action
import (
"fmt"
"github.com/slack-go/slack"
"github.com/slack-go/slack/socketmode"
"github.com/spf13/viper"
"go.uber.org/zap"
"houston/appcontext"
"houston/common/metrics"
incidentHelper "houston/common/util"
"houston/common/util/structUtil"
"houston/internal"
"houston/internal/processor/action/view"
"houston/logger"
"houston/pkg/slackbot"
incidentService "houston/service/incident/impl"
service "houston/service/request"
"strconv"
"strings"
"time"
)
const setSeverityActionLogTag = "[set_severity_command_action]"
type SetSeverityCommandAction struct {
socketModeClient *socketmode.Client
slackBot *slackbot.Client
}
func NewSetSeverityCommandAction(
socketModeClient *socketmode.Client,
slackBot *slackbot.Client,
) *SetSeverityCommandAction {
return &SetSeverityCommandAction{
socketModeClient: socketModeClient,
slackBot: slackBot,
}
}
func (action *SetSeverityCommandAction) PerformAction(evt *socketmode.Event) {
cmd, ok := evt.Data.(slack.SlashCommand)
logger.Info("processing houston command", zap.Any("payload", cmd))
if !ok {
logger.Error("event data to slash command conversion failed", zap.Any("data", evt))
return
}
err := action.setSeverity(cmd, strings.TrimSpace(cmd.Text[len(internal.SetSeverityParam):]))
if err != nil {
err := appcontext.GetSlackService().PostEphemeralByChannelID(err.Error(), cmd.UserID, false, cmd.ChannelID)
if err != nil {
logger.Error(fmt.Sprintf("%s failed to post ephemeral for create incident error. %+v", resolveIncidentActionLogTag, err))
}
}
action.socketModeClient.Ack(*evt.Request)
}
// todo: this method has to be removed and usage has to be replaced with update incident V2 once update incident refactor goes live.
func (action *SetSeverityCommandAction) setSeverity(cmd slack.SlashCommand, severity string) error {
logger.Info(fmt.Sprintf("%s received request to update the severity to %s", setSeverityActionLogTag, severity))
return executeForHoustonChannel(cmd, func() error {
incidentEntity, err := appcontext.GetIncidentService().GetIncidentByChannelID(cmd.ChannelID)
if err != nil {
logger.Error(fmt.Sprintf("%s failed to fetch incident entity with channel ID: %s. %+v", setSeverityActionLogTag, cmd.ChannelID, err))
return genericBackendError
}
if incidentEntity == nil {
logger.Error(fmt.Sprintf("%s no entry found for incident with channel ID: %s in DB", setSeverityActionLogTag, cmd.ChannelID))
return genericBackendError
}
severityEntity, err := appcontext.GetSeverityRepo().FindSeverityByName(severity)
if err != nil {
logger.Error(fmt.Sprintf("%s failed to fetch severity entity for: %s. %+v", setSeverityActionLogTag, severity, err))
return genericBackendError
}
if severityEntity == nil {
logger.Error(fmt.Sprintf("%s no DB entity found for %s. %+v", setSeverityActionLogTag, severity, err))
return fmt.Errorf("%s is not a valid severity", severity)
}
if viper.GetBool("UPDATE_INCIDENT_V2_ENABLED") {
incidentSeverityId := severityEntity.ID
teamEntity, _, incidentStatusEntity, incidentChannels, err := appcontext.GetIncidentService().FetchAllEntitiesForIncident(incidentEntity)
if err != nil {
logger.Error(fmt.Sprintf("error in fetching entities for incident with id: %d %v", incidentEntity.ID, err))
return err
}
if viper.GetBool("ENABLE_DE_ESCALATION_JUSTIFICATION") {
if incidentSeverityId > incidentEntity.SeverityId {
justificationMetadata := JustificationMetadata{SeverityId: incidentSeverityId, ChannelId: cmd.ChannelID}
stringData, stringErr := structUtil.StructToString(justificationMetadata)
if stringErr != nil {
logger.Error(fmt.Sprintf("error in converting justification metadata to string for incident id: %d", incidentEntity.ID), zap.Error(stringErr))
}
_, viewErr := action.socketModeClient.OpenView(cmd.TriggerID, view.CreateSeverityJustificationBlock(stringData))
if viewErr != nil {
logger.Error(fmt.Sprintf("error in opening justification view for incident with id: %d", incidentEntity.ID), zap.Error(viewErr))
return viewErr
}
return nil
}
}
if err := appcontext.GetIncidentService().UpdateSeverityId(
service.UpdateIncidentRequest{
Id: incidentEntity.ID,
SeverityId: strconv.Itoa(int(incidentSeverityId)),
},
cmd.UserID,
incidentEntity,
teamEntity,
incidentStatusEntity,
incidentChannels,
); err != nil {
logger.Error(fmt.Sprintf("error in updating severity: %v", err))
metrics.PublishHoustonFlowFailureMetrics(incidentService.UPDATE_INCIDENT_SEVERITY, err.Error())
return err
}
return nil
}
incidentEntity.SeverityId = severityEntity.ID
incidentEntity.UpdatedBy = cmd.UserID
incidentEntity.SeverityTat = time.Now().AddDate(0, 0, severityEntity.Sla)
err = appcontext.GetIncidentRepo().UpdateIncident(incidentEntity)
if err != nil {
logger.Error(fmt.Sprintf("%s failed to update severity for incident: %s. %+v", setSeverityActionLogTag, incidentEntity.IncidentName, err))
return fmt.Errorf("failed to update severity for incident: %s", incidentEntity.IncidentName)
}
teamEntity, err := appcontext.GetTeamRepo().FindTeamById(incidentEntity.TeamId)
if err != nil {
logger.Error(fmt.Sprintf("%s error in finding team entity by team name %d. %+v", setSeverityActionLogTag, incidentEntity.TeamId, err))
return genericBackendError
}
if teamEntity == nil {
logger.Error(fmt.Sprintf("%s invalid team name %d. No entity found in DB", setSeverityActionLogTag, incidentEntity.TeamId))
return genericBackendError
}
for _, o := range severityEntity.SlackUserIds {
action.slackBot.InviteUsersToConversation(cmd.ChannelID, o)
}
go func() {
msgOption := slack.MsgOptionText(fmt.Sprintf("<@%s> *>* `set severity to %s (%s)`", cmd.UserID, severityEntity.Name, severityEntity.Description), false)
_, _, errMessage := action.socketModeClient.PostMessage(cmd.ChannelID, msgOption)
if errMessage != nil {
logger.Error("post response failed for IncidentUpdateSeverity", zap.Error(errMessage))
return
}
txt := fmt.Sprintf("set the channel topic: *%s · %s (%s) %s* | %s", teamEntity.Name, severityEntity.Name, severityEntity.Description, incidentEntity.IncidentName, incidentEntity.Title)
att := slack.Attachment{
Text: txt,
Color: "#808080", // Grey color code
MarkdownIn: []string{"txt"}, // Define which fields support markdown
}
_, _, errMessage = action.socketModeClient.PostMessage(cmd.ChannelID, slack.MsgOptionAttachments(att))
if errMessage != nil {
logger.Error(fmt.Sprintf("%s post response failed for IncidentUpdateType. %+v", setSeverityActionLogTag, zap.Error(errMessage)))
return
}
topic := fmt.Sprintf("%s-%s(%s) Incident-%d | %s",
teamEntity.Name, severityEntity.Name, severityEntity.Description, incidentEntity.ID, incidentEntity.Title,
)
action.slackBot.SetChannelTopic(cmd.ChannelID, topic)
}()
incidentHelper.TagPseOrDevOncallToIncident(
cmd.ChannelID,
severityEntity,
teamEntity,
action.slackBot,
action.socketModeClient,
)
err = incidentHelper.AssignResponderToIncident(
appcontext.GetIncidentRepo(),
incidentEntity,
teamEntity,
severityEntity,
action.socketModeClient,
cmd.UserID,
)
if err != nil {
logger.Error(fmt.Sprintf("%s Error while assigning responder to the incident %+v", setSeverityActionLogTag, zap.Error(err)))
return fmt.Errorf("severity is set to %s. Failed to assign responder post severity update", severity)
}
go appcontext.GetIncidentService().SendAlert(incidentEntity)
return nil
})
}