* 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
196 lines
7.5 KiB
Go
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
|
|
})
|
|
}
|