INFRA-2828 : Drop unused tables, update migrations and removed unused code modules (#367)
This commit is contained in:
@@ -5,7 +5,6 @@ import (
|
||||
"houston/appcontext"
|
||||
"houston/common/util"
|
||||
"houston/internal/cron"
|
||||
"houston/internal/diagnostic"
|
||||
"houston/internal/processor"
|
||||
"houston/internal/resolver"
|
||||
"houston/model/incident"
|
||||
@@ -40,7 +39,6 @@ type slackHandler struct {
|
||||
memberJoinCallbackProcessor *processor.MemberJoinedCallbackEventProcessor
|
||||
blockActionProcessor *processor.BlockActionProcessor
|
||||
viewSubmissionProcessor *processor.ViewSubmissionProcessor
|
||||
diagnosticCommandProcessor *processor.DiagnosticCommandProcessor
|
||||
userChangeEventProcessor *processor.UserChangeEventProcessor
|
||||
houstonCommandResolver *resolver.HoustonCommandResolver
|
||||
}
|
||||
@@ -54,8 +52,6 @@ func NewSlackHandler(gormClient *gorm.DB, socketModeClient *socketmode.Client) *
|
||||
userService := user.NewUserRepository(gormClient)
|
||||
shedlockService := shedlock.NewShedlockRepository(gormClient)
|
||||
slackbotClient := slackbot.NewSlackClient(socketModeClient)
|
||||
grafanaRepository := diagnostic.NewDiagnoseRepository(gormClient)
|
||||
diagnosticCommandProcessor := processor.NewDiagnosticCommandProcessor(socketModeClient, grafanaRepository)
|
||||
incidentServiceV2 := incidentServiceV2.NewIncidentServiceV2(gormClient)
|
||||
slackService := slack2.NewSlackService()
|
||||
// new services
|
||||
@@ -84,9 +80,8 @@ func NewSlackHandler(gormClient *gorm.DB, socketModeClient *socketmode.Client) *
|
||||
}
|
||||
|
||||
return &slackHandler{
|
||||
socketModeClient: socketModeClient,
|
||||
slashCommandProcessor: slashCommandProcessor,
|
||||
diagnosticCommandProcessor: diagnosticCommandProcessor,
|
||||
socketModeClient: socketModeClient,
|
||||
slashCommandProcessor: slashCommandProcessor,
|
||||
memberJoinCallbackProcessor: processor.NewMemberJoinedCallbackEventProcessor(
|
||||
socketModeClient, incidentService, teamService, severityService,
|
||||
),
|
||||
@@ -99,7 +94,7 @@ func NewSlackHandler(gormClient *gorm.DB, socketModeClient *socketmode.Client) *
|
||||
userChangeEventProcessor: processor.NewUserChangeEventProcessor(
|
||||
socketModeClient, userService,
|
||||
),
|
||||
houstonCommandResolver: resolver.NewHoustonCommandResolver(diagnosticCommandProcessor, socketModeClient, slackbotClient, rcaService),
|
||||
houstonCommandResolver: resolver.NewHoustonCommandResolver(socketModeClient, slackbotClient, rcaService),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
8
db/migration/000013_drop_unused_tables.up.sql
Normal file
8
db/migration/000013_drop_unused_tables.up.sql
Normal file
@@ -0,0 +1,8 @@
|
||||
DROP TABLE IF EXISTS role;
|
||||
DROP TABLE IF EXISTS team_tag;
|
||||
DROP TABLE IF EXISTS dbz_heartbeat;
|
||||
DROP TABLE IF EXISTS deployment_config_mapping;
|
||||
DROP TABLE IF EXISTS deployment_mapping;
|
||||
DROP TABLE IF EXISTS diagnose_runbook;
|
||||
DROP TABLE IF EXISTS grafana_mapping;
|
||||
DROP TABLE IF EXISTS kibana_mapping;
|
||||
@@ -1,57 +0,0 @@
|
||||
package diagnostic
|
||||
|
||||
import (
|
||||
"gorm.io/gorm"
|
||||
"houston/internal/diagnostic/models/entity"
|
||||
"houston/model/severity"
|
||||
)
|
||||
|
||||
type Repository struct {
|
||||
gormClient *gorm.DB
|
||||
severityService *severity.Repository
|
||||
}
|
||||
|
||||
func NewDiagnoseRepository(gormClient *gorm.DB) *Repository {
|
||||
return &Repository{
|
||||
gormClient: gormClient,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Repository) GetGrafanaLinksForAlert(alertId string) ([]entity.GrafanaDiagnosticEntity, error) {
|
||||
var grafanaLink []entity.GrafanaDiagnosticEntity
|
||||
|
||||
result := r.gormClient.Table("grafana_mapping").Find(&grafanaLink, "alert_id = ?", alertId)
|
||||
if result.Error != nil {
|
||||
return nil, result.Error
|
||||
}
|
||||
if result.RowsAffected == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return grafanaLink, nil
|
||||
}
|
||||
|
||||
func (r *Repository) GetDeploymentPortalDetails(alertId string) ([]entity.DeploymentDiagnosticEntity, error) {
|
||||
var portalDetails []entity.DeploymentDiagnosticEntity
|
||||
|
||||
result := r.gormClient.Table("deployment_mapping").Find(&portalDetails, "alert_id = ?", alertId)
|
||||
if result.Error != nil {
|
||||
return nil, result.Error
|
||||
}
|
||||
if result.RowsAffected == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return portalDetails, nil
|
||||
}
|
||||
|
||||
func (r *Repository) GetDeploymentConfigDetails(alertId string) ([]entity.DeploymentConfigEntity, error) {
|
||||
var portalDetails []entity.DeploymentConfigEntity
|
||||
|
||||
result := r.gormClient.Table("deployment_config_mapping").Find(&portalDetails, "alert_id = ?", alertId)
|
||||
if result.Error != nil {
|
||||
return nil, result.Error
|
||||
}
|
||||
if result.RowsAffected == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return portalDetails, nil
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
package diagnostic
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/google/uuid"
|
||||
"github.com/slack-go/slack"
|
||||
"houston/internal/diagnostic/models"
|
||||
)
|
||||
|
||||
type DataAssembler interface {
|
||||
HandleData(runbooks []models.Runbook) string
|
||||
}
|
||||
|
||||
type AssemblerData struct {
|
||||
}
|
||||
|
||||
type DataAssemblerImpl struct {
|
||||
assemblerData AssemblerData
|
||||
}
|
||||
|
||||
type DataAssembler2Impl struct {
|
||||
assemblerData AssemblerData
|
||||
}
|
||||
|
||||
func NewDiagnosticDataAssembler() *DataAssemblerImpl {
|
||||
return &DataAssemblerImpl{
|
||||
assemblerData: AssemblerData{},
|
||||
}
|
||||
}
|
||||
|
||||
func NewDiagnosticDataAssembler2() *DataAssembler2Impl {
|
||||
return &DataAssembler2Impl{
|
||||
assemblerData: AssemblerData{},
|
||||
}
|
||||
}
|
||||
|
||||
func (gah *DataAssemblerImpl) HandleData(runbooks []models.Runbook) (slack.MsgOption, []slack.Attachment) {
|
||||
var messageBlock slack.MsgOption
|
||||
attachments := make([]slack.Attachment, 0)
|
||||
for _, runbook := range runbooks {
|
||||
if runbook.ImagePaths != nil {
|
||||
imageMessages := make([]slack.Block, 0)
|
||||
for it, images := range runbook.ImagePaths {
|
||||
imageBlock := slack.NewImageBlock(images, "", "id"+string(rune(it)), &slack.TextBlockObject{Type: "plain_text", Text: "abc"})
|
||||
imageMessages = append(imageMessages, imageBlock)
|
||||
}
|
||||
messageBlock = slack.MsgOptionBlocks(imageMessages...)
|
||||
}
|
||||
if runbook.TextSnippets != nil {
|
||||
attachment := slack.Attachment{
|
||||
Text: runbook.TextSnippets[0],
|
||||
}
|
||||
attachments = append(attachments, attachment)
|
||||
}
|
||||
}
|
||||
return messageBlock, attachments
|
||||
}
|
||||
|
||||
func (gah *DataAssembler2Impl) HandleData(runbooks []models.Runbook) (slack.MsgOption, []slack.Attachment) {
|
||||
var messageBlock slack.MsgOption
|
||||
messages := make([]slack.Block, 0)
|
||||
attachments := make([]slack.Attachment, 0)
|
||||
for _, runbook := range runbooks {
|
||||
if runbook.ImagePaths != nil {
|
||||
for _, images := range runbook.ImagePaths {
|
||||
id, _ := uuid.NewUUID()
|
||||
textBlock := &slack.TextBlockObject{
|
||||
Type: "plain_text",
|
||||
Text: "image",
|
||||
}
|
||||
imageSectionBlock := slack.NewImageBlock(images, "", id.String(), textBlock)
|
||||
messages = append(messages, imageSectionBlock)
|
||||
}
|
||||
}
|
||||
if runbook.TextSnippets != nil {
|
||||
var message bytes.Buffer
|
||||
for it, text := range runbook.TextSnippets {
|
||||
if it == 0 {
|
||||
message.WriteString(text)
|
||||
continue
|
||||
}
|
||||
message.WriteString("\n>" + text)
|
||||
}
|
||||
textBlock := slack.NewTextBlockObject("mrkdwn", message.String(), false, false)
|
||||
block := slack.NewSectionBlock(textBlock, nil, nil)
|
||||
messages = append(messages, block)
|
||||
}
|
||||
}
|
||||
messageBlock = slack.MsgOptionBlocks(messages...)
|
||||
return messageBlock, attachments
|
||||
}
|
||||
@@ -1,160 +0,0 @@
|
||||
package diagnostic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/joho/godotenv"
|
||||
"github.com/spf13/viper"
|
||||
"go.uber.org/zap"
|
||||
"houston/config"
|
||||
"houston/internal/clients"
|
||||
"houston/logger"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func DeploymentConfigHandler(repo *Repository, alertId string) []string {
|
||||
deploymentConfigDetails, _ := repo.GetDeploymentConfigDetails(alertId)
|
||||
config.LoadHoustonConfig()
|
||||
godotenv.Load()
|
||||
logger.Info("DeploymentConfigHandler")
|
||||
client := clients.NewHttpClient()
|
||||
result := make([]string, 0)
|
||||
configClient := NewConfigClient(client.HttpClient)
|
||||
result = append(result, ":ghost: *New and Updated configs* :ghost:")
|
||||
for _, deploymentConfig := range deploymentConfigDetails {
|
||||
baseUrl := deploymentConfig.DeploymentPortalUrl
|
||||
bearerToken := getBearerTokenForConfigBaseUrl(baseUrl)
|
||||
config := DeploymentConfig{
|
||||
BaseUrl: baseUrl,
|
||||
BearerToken: bearerToken,
|
||||
}
|
||||
configListResponse, _ := configClient.getConfigResponse(config, deploymentConfig.ManifestId)
|
||||
if configListResponse != nil && len(configListResponse) > 2 {
|
||||
deploymentConfig1, _ := configClient.getConfigResponseForVersion(config, deploymentConfig.ManifestId, strconv.Itoa(configListResponse[0]))
|
||||
deploymentConfig2, _ := configClient.getConfigResponseForVersion(config, deploymentConfig.ManifestId, strconv.Itoa(configListResponse[1]))
|
||||
diff := calculateDiff(deploymentConfig1, deploymentConfig2)
|
||||
result = append(result, diff...)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (m *ConfigClient) getConfigResponseForVersion(config DeploymentConfig, manifestId string, version string) (*ConfigResponse, error) {
|
||||
client := m.HttpClient
|
||||
req, _ := http.NewRequest("GET", fmt.Sprintf("%sapi/manifest/%s/version/%s", config.BaseUrl, manifestId, version), nil)
|
||||
req.Header.Add("X_AUTH_TOKEN", fmt.Sprintf("%s", config.BearerToken))
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
m.Logger.Error(err.Error())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
responseBody, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
m.Logger.Error(err.Error())
|
||||
return nil, err
|
||||
}
|
||||
var response ConfigResponse
|
||||
err = json.Unmarshal(responseBody, &response)
|
||||
if err != nil {
|
||||
m.Logger.Error(err.Error())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
func (m *ConfigClient) getConfigResponse(config DeploymentConfig, manifestId string) (ConfigList, error) {
|
||||
client := m.HttpClient
|
||||
req, _ := http.NewRequest("GET", fmt.Sprintf("%sapi/manifest/%s/version/list", config.BaseUrl, manifestId), nil)
|
||||
req.Header.Add("X_AUTH_TOKEN", fmt.Sprintf("%s", config.BearerToken))
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
m.Logger.Error(err.Error())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
responseBody, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
m.Logger.Error(err.Error())
|
||||
return nil, err
|
||||
}
|
||||
var response ConfigList
|
||||
err = json.Unmarshal(responseBody, &response)
|
||||
if err != nil {
|
||||
m.Logger.Error(err.Error())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func calculateDiff(response1 *ConfigResponse, response2 *ConfigResponse) []string {
|
||||
if response1 == nil || response2 == nil {
|
||||
logger.Info("either response1 is null or response1 is")
|
||||
}
|
||||
diff := make([]string, 0)
|
||||
var result = make(map[string]string)
|
||||
for _, v := range response1.EnvironmentVariables {
|
||||
result[v.Name] = v.Value
|
||||
}
|
||||
for _, variable := range response2.EnvironmentVariables {
|
||||
value, ok := result[variable.Name]
|
||||
if ok {
|
||||
if value != variable.Value {
|
||||
diff = append(diff, variable.Name+", New: "+value+" old: "+variable.Value)
|
||||
}
|
||||
} else {
|
||||
diff = append(diff, variable.Name+", New: "+value+" old: "+variable.Value)
|
||||
}
|
||||
delete(result, variable.Name)
|
||||
}
|
||||
for key, variable := range result {
|
||||
diff = append(diff, key+", New: "+variable)
|
||||
}
|
||||
return diff
|
||||
}
|
||||
|
||||
func getBearerTokenForConfigBaseUrl(baseUrl string) string {
|
||||
switch url := baseUrl; url {
|
||||
case "https://sa-deployment-portal-backend.cmd.navi-tech.in/":
|
||||
return viper.GetString("config.sa.keys")
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func NewConfigClient(httpClient *http.Client) *ConfigClient {
|
||||
return &ConfigClient{
|
||||
HttpClient: httpClient,
|
||||
}
|
||||
}
|
||||
|
||||
type ConfigList []int
|
||||
|
||||
type ConfigClient struct {
|
||||
HttpClient *http.Client
|
||||
Logger *zap.Logger
|
||||
baseUrl string
|
||||
config GoCDConnectionConfig
|
||||
}
|
||||
|
||||
type ConfigResponse struct {
|
||||
Name string `json:"name"`
|
||||
EnvironmentVariables []EnvironmentVariable `json:"environmentVariables"`
|
||||
}
|
||||
|
||||
type EnvironmentVariable struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Value string `json:"value,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
}
|
||||
|
||||
type DeploymentConfig struct {
|
||||
BaseUrl string
|
||||
BearerToken string
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
package diagnostic
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/joho/godotenv"
|
||||
"github.com/spf13/viper"
|
||||
"houston/config"
|
||||
"houston/internal/clients"
|
||||
"houston/logger"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
func DeploymentPortalHandler(repo *Repository, alertId string) []string {
|
||||
deplymentPortalDetails, _ := repo.GetDeploymentPortalDetails(alertId)
|
||||
config.LoadHoustonConfig()
|
||||
godotenv.Load()
|
||||
|
||||
client := clients.NewHttpClient()
|
||||
result := make([]string, 0)
|
||||
goCDClient := NewGoCDClient(client.HttpClient)
|
||||
for _, deploymentPortal := range deplymentPortalDetails {
|
||||
baseUrl := deploymentPortal.DeploymentPortalUrl
|
||||
bearerToken := getBearerTokenForBaseUrl(baseUrl)
|
||||
config := GoCDConnectionConfig{
|
||||
BaseUrl: baseUrl,
|
||||
BearerToken: bearerToken,
|
||||
}
|
||||
response, _ := goCDClient.getPipelineHistoryResponse(config, deploymentPortal.ServiceName)
|
||||
if len(response.Pipelines) > 0 {
|
||||
result = append(result, " :ghost: *"+response.Pipelines[0].Name+"* :ghost:")
|
||||
for _, pipeLine := range response.Pipelines {
|
||||
var singleDeployment bytes.Buffer
|
||||
singleDeployment.WriteString("Approver ")
|
||||
singleDeployment.WriteString(pipeLine.BuildCause.Approver)
|
||||
singleDeployment.WriteString(" Message: ")
|
||||
singleDeployment.WriteString(pipeLine.BuildCause.TriggerMessage)
|
||||
singleDeployment.WriteString(" on ")
|
||||
singleDeployment.WriteString(getDate(pipeLine.ScheduledDate))
|
||||
result = append(result, singleDeployment.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func getDate(date int64) string {
|
||||
loc, _ := time.LoadLocation("Asia/Kolkata")
|
||||
tm := time.Unix(0, date*int64(time.Millisecond)).In(loc)
|
||||
tm.Format("2006-02-01 15:01:05")
|
||||
return tm.String()
|
||||
}
|
||||
|
||||
func getBearerTokenForBaseUrl(baseUrl string) string {
|
||||
switch url := baseUrl; url {
|
||||
case "https://sa-pipelines.cmd.navi-tech.in":
|
||||
return viper.GetString("gocd.sa.bearer.token")
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
type GoCDClient struct {
|
||||
HttpClient *http.Client
|
||||
baseUrl string
|
||||
config GoCDConnectionConfig
|
||||
}
|
||||
|
||||
type GoCDConnectionConfig struct {
|
||||
BaseUrl string
|
||||
BearerToken string
|
||||
}
|
||||
|
||||
func NewGoCDClient(httpClient *http.Client) *GoCDClient {
|
||||
return &GoCDClient{
|
||||
HttpClient: httpClient,
|
||||
}
|
||||
}
|
||||
|
||||
type GoCdResponse struct {
|
||||
Pipelines []GoCdStageRun `json:"pipelines,omitempty"`
|
||||
}
|
||||
|
||||
type GoCdStageRun struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
StageCounter int32 `json:"counter,omitempty"`
|
||||
ScheduledDate int64 `json:"scheduled_date,omitempty"`
|
||||
BuildCause GoCdBuildCause `json:"build_cause,omitempty"`
|
||||
}
|
||||
|
||||
type GoCdBuildCause struct {
|
||||
TriggerMessage string `json:"trigger_message,omitempty"`
|
||||
Approver string `json:"approver,omitempty"`
|
||||
}
|
||||
|
||||
func (m *GoCDClient) getPipelineHistoryResponse(config GoCDConnectionConfig, pipelineName string) (*GoCdResponse, error) {
|
||||
client := m.HttpClient
|
||||
req, _ := http.NewRequest("GET", fmt.Sprintf("%s/go/api/pipelines/%s/history", config.BaseUrl, pipelineName), nil)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("bearer %s", config.BearerToken))
|
||||
req.Header.Add("Accept", "application/vnd.go.cd.v1+json")
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
responseBody, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var response GoCdResponse
|
||||
err = json.Unmarshal(responseBody, &response)
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &response, nil
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -1,15 +0,0 @@
|
||||
package entity
|
||||
|
||||
import "gorm.io/gorm"
|
||||
|
||||
type DeploymentConfigEntity struct {
|
||||
gorm.Model
|
||||
AlertId string `gorm:"column:alert_id"`
|
||||
ManifestId string `gorm:"column:manifest_id"`
|
||||
DeploymentPortalUrl string `gorm:"column:deployment_portal_url"`
|
||||
Priority int `gorm:"column:priority"`
|
||||
}
|
||||
|
||||
func (DeploymentConfigEntity) TableName() string {
|
||||
return "deployment_config_mapping"
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package entity
|
||||
|
||||
import "gorm.io/gorm"
|
||||
|
||||
type DeploymentDiagnosticEntity struct {
|
||||
gorm.Model
|
||||
AlertId string `gorm:"column:alert_id"`
|
||||
ServiceName string `gorm:"column:service_name"`
|
||||
DeploymentPortalUrl string `gorm:"column:deployment_portal_url"`
|
||||
Priority int `gorm:"column:priority"`
|
||||
}
|
||||
|
||||
func (DeploymentDiagnosticEntity) TableName() string {
|
||||
return "deployment_mapping"
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package entity
|
||||
|
||||
import "gorm.io/gorm"
|
||||
|
||||
type DiagnoseEntity struct {
|
||||
gorm.Model
|
||||
AlertId string `gorm:"column:alert_id"`
|
||||
AlertName string `gorm:"column:alert_name`
|
||||
Description string `gorm:"column:description`
|
||||
IsActive bool `gorm:"column:is_active"`
|
||||
}
|
||||
|
||||
func (DiagnoseEntity) TableName() string {
|
||||
return "diagnose_runbook"
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
package entity
|
||||
@@ -1,14 +0,0 @@
|
||||
package entity
|
||||
|
||||
import "gorm.io/gorm"
|
||||
|
||||
type GrafanaDiagnosticEntity struct {
|
||||
gorm.Model
|
||||
AlertId string `gorm:"column:alert_id"`
|
||||
EndpointUrl string `gorm:"column:endpoint_url"`
|
||||
Priority int `gorm:"column:priority"`
|
||||
}
|
||||
|
||||
func (GrafanaDiagnosticEntity) TableName() string {
|
||||
return "grafana_mapping"
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package entity
|
||||
|
||||
import "gorm.io/gorm"
|
||||
|
||||
type KibanaDiagnosticEntity struct {
|
||||
gorm.Model
|
||||
AlertId string `gorm:"column:alert_id"`
|
||||
KibanaLink string `gorm:"column:kibana_link"`
|
||||
Priority int `gorm:"column:priority"`
|
||||
CustomQuery string `gorm:"column:custom_query"`
|
||||
}
|
||||
|
||||
func (KibanaDiagnosticEntity) TableName() string {
|
||||
return "kibana_mapping"
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package models
|
||||
|
||||
type GoCdResponse struct {
|
||||
PipelineName string `json:"pipelineName,omitempty"`
|
||||
Groups []GoCdGroups `json:"groups,omitempty"`
|
||||
}
|
||||
|
||||
type GoCdGroups struct {
|
||||
History []GoCdPipelineRun `json:"history,omitempty"`
|
||||
}
|
||||
|
||||
type GoCdPipelineRun struct {
|
||||
BuildCause string `json:"buildCauseBy,omitempty"`
|
||||
ScheduledDate string `json:"scheduled_date,omitempty"`
|
||||
Stages []GoCdStageRun `json:"stages,omitempty"`
|
||||
}
|
||||
|
||||
type GoCdStageRun struct {
|
||||
Name string `json:"stageName,omitempty"`
|
||||
StageCounter string `json:"stageCounter,omitempty"`
|
||||
StageStatus string `json:"stageStatus,omitempty"`
|
||||
ScheduledDate string `json:"scheduled_date,omitempty"`
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
package models
|
||||
|
||||
type Runbook struct {
|
||||
links []string // links to be shown in the printed runbook on slack
|
||||
TextSnippets []string // snippets to be shown in printed runbook on slack
|
||||
ImagePaths []string // images that needs to be shown in the runbook
|
||||
filePaths []string // if any files needs to be uploaded along with the runbook
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
package diagnostic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/google/uuid"
|
||||
"github.com/joho/godotenv"
|
||||
"github.com/spf13/viper"
|
||||
"go.uber.org/zap"
|
||||
"houston/config"
|
||||
"houston/logger"
|
||||
"houston/pkg/s3"
|
||||
"image"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/chromedp/cdproto/network"
|
||||
"github.com/chromedp/chromedp"
|
||||
)
|
||||
|
||||
func getImageFromFilePath(filePath string) (image.Image, error) {
|
||||
f, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
image, _, err := image.Decode(f)
|
||||
return image, err
|
||||
}
|
||||
|
||||
func TakeScreenshotAndUpload(repo *Repository, alertId string) []string {
|
||||
grafanaLinks, _ := repo.GetGrafanaLinksForAlert(alertId)
|
||||
logger.Info("taking screenshot")
|
||||
fileLinkList := make([]string, 0)
|
||||
for _, links := range grafanaLinks {
|
||||
takeScreenShotUrl(&fileLinkList, links.EndpointUrl)
|
||||
}
|
||||
return fileLinkList
|
||||
}
|
||||
|
||||
func takeScreenShotUrl(fileList *[]string, url string) {
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
}
|
||||
token := fmt.Sprintf("Bearer %s", viper.GetString("grafana.token"))
|
||||
req.Header.Set("Authorization", token)
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
}
|
||||
id := uuid.New()
|
||||
if err := os.MkdirAll("images", os.ModePerm); err != nil {
|
||||
logger.Error(err.Error())
|
||||
}
|
||||
fileName := "images/" + id.String() + ".jpg"
|
||||
file, err := os.Create(fileName)
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
}
|
||||
defer file.Close()
|
||||
_, err = io.Copy(file, resp.Body)
|
||||
link, err := upload(fileName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
os.Remove(fileName)
|
||||
*fileList = append(*fileList, link)
|
||||
}
|
||||
|
||||
func takeScreenShot(fileList *[]string, url string) {
|
||||
ctx, cancel := chromedp.NewContext(context.Background())
|
||||
defer cancel()
|
||||
|
||||
var buf []byte
|
||||
if err := chromedp.Run(ctx, setHeaders(url, &buf)); err != nil {
|
||||
logger.Fatal(err.Error())
|
||||
}
|
||||
id := uuid.New()
|
||||
if err := os.MkdirAll("images", os.ModePerm); err != nil {
|
||||
logger.Fatal(err.Error())
|
||||
}
|
||||
fileName := "images/" + id.String() + ".jpg"
|
||||
if err := os.WriteFile(fileName, buf, 0644); err != nil {
|
||||
logger.Fatal(err.Error())
|
||||
}
|
||||
link, err := upload(fileName)
|
||||
if err != nil {
|
||||
logger.Error("failed", zap.Error(err))
|
||||
return
|
||||
}
|
||||
os.Remove(fileName)
|
||||
*fileList = append(*fileList, link)
|
||||
}
|
||||
|
||||
func upload(fileName string) (string, error) {
|
||||
s3Operations := s3.NewS3Operations()
|
||||
s3BucketName := viper.GetString("S3_BUCKET_NAME")
|
||||
output, uploadErr := s3Operations.UploadFile(s3BucketName, filepath.Join(), fileName, fileName)
|
||||
if uploadErr != nil {
|
||||
logger.Info("upload failed for grafana screenshot")
|
||||
logger.Error(uploadErr.Error())
|
||||
logger.Error("upload failed", zap.Error(uploadErr), zap.Any("output", output))
|
||||
return "", uploadErr
|
||||
}
|
||||
link, err := s3Operations.GetFileLink(s3BucketName, fileName)
|
||||
if err != nil {
|
||||
logger.Error("upload failed", zap.Error(err), zap.Any("output", output))
|
||||
return "", err
|
||||
}
|
||||
|
||||
logger.Info("upload success", zap.String("link", link), zap.Any("s3_output", output), zap.Error(err))
|
||||
return link, nil
|
||||
}
|
||||
|
||||
func setHeaders(host string, imageBuf *[]byte) chromedp.Tasks {
|
||||
config.LoadHoustonConfig()
|
||||
godotenv.Load()
|
||||
token := fmt.Sprintf("Bearer %s", viper.GetString("grafana.token"))
|
||||
headers := map[string]interface{}{
|
||||
"Authorization": token,
|
||||
}
|
||||
return chromedp.Tasks{
|
||||
network.Enable(),
|
||||
network.SetExtraHTTPHeaders(headers),
|
||||
chromedp.Navigate(host), //chromedp.WaitVisible(`#reactRoot > div > main > div:nth-child(3) > div > div > div > div > div > div > div > div > div:nth-child(33) > div > section > div:nth-child(2)`),
|
||||
chromedp.Sleep(5 * time.Second), // chromedp.WaitVisible(`body`, chromedp.BySearch),
|
||||
chromedp.ActionFunc(func(context.Context) error {
|
||||
return nil
|
||||
}),
|
||||
chromedp.FullScreenshot(imageBuf, 95),
|
||||
}
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
package action
|
||||
|
||||
import (
|
||||
"github.com/slack-go/slack/socketmode"
|
||||
"go.uber.org/zap"
|
||||
"houston/internal/diagnostic"
|
||||
"houston/internal/diagnostic/models"
|
||||
)
|
||||
|
||||
type DiagnosticCommandActionHandler interface {
|
||||
HandleAction(e *socketmode.Event, alertname string) models.Runbook
|
||||
}
|
||||
|
||||
type HandlerData struct {
|
||||
logger *zap.Logger
|
||||
client *socketmode.Client
|
||||
repository *diagnostic.Repository
|
||||
}
|
||||
|
||||
type GrafanaActionHandler struct {
|
||||
grafanaHandlerData HandlerData
|
||||
}
|
||||
|
||||
type KibanaActionHandler struct {
|
||||
kibanaHandlerData HandlerData
|
||||
}
|
||||
|
||||
type DeploymentPortalHandler struct {
|
||||
deploymentPortalHandlerData HandlerData
|
||||
}
|
||||
|
||||
type DeploymentConfigHandler struct {
|
||||
deploymentConfigHandlerData HandlerData
|
||||
}
|
||||
|
||||
func NewGrafanaActionHandler(client *socketmode.Client, repository *diagnostic.Repository) *GrafanaActionHandler {
|
||||
return &GrafanaActionHandler{
|
||||
grafanaHandlerData: HandlerData{
|
||||
|
||||
client: client,
|
||||
repository: repository,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func NewKibanaActionHandler(client *socketmode.Client) *KibanaActionHandler {
|
||||
return &KibanaActionHandler{
|
||||
kibanaHandlerData: HandlerData{
|
||||
client: client,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func NewDeploymentPortalActionHandler(client *socketmode.Client, repository *diagnostic.Repository) *DeploymentPortalHandler {
|
||||
return &DeploymentPortalHandler{
|
||||
deploymentPortalHandlerData: HandlerData{
|
||||
|
||||
client: client,
|
||||
repository: repository,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func NewDeploymentConfigActionHandler(client *socketmode.Client, repository *diagnostic.Repository) *DeploymentConfigHandler {
|
||||
return &DeploymentConfigHandler{
|
||||
deploymentConfigHandlerData: HandlerData{
|
||||
|
||||
client: client,
|
||||
repository: repository,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (gah *GrafanaActionHandler) HandleAction(e *socketmode.Event, alertName string) models.Runbook {
|
||||
imagePaths := diagnostic.TakeScreenshotAndUpload(gah.grafanaHandlerData.repository, alertName)
|
||||
var runbook models.Runbook
|
||||
runbook.ImagePaths = imagePaths
|
||||
return runbook
|
||||
}
|
||||
|
||||
func (kah *KibanaActionHandler) HandleAction(e *socketmode.Event, alertName string) models.Runbook {
|
||||
var runbook models.Runbook
|
||||
kibanaResponse := diagnostic.GetKibana()
|
||||
kibanaResponseArray := make([]string, 0)
|
||||
kibanaResponseArray = append(kibanaResponseArray, kibanaResponse)
|
||||
runbook.TextSnippets = kibanaResponseArray
|
||||
return runbook
|
||||
}
|
||||
|
||||
func (dph *DeploymentPortalHandler) HandleAction(e *socketmode.Event, alertName string) models.Runbook {
|
||||
var runbook models.Runbook
|
||||
deploymentDetails := diagnostic.DeploymentPortalHandler(dph.deploymentPortalHandlerData.repository, alertName)
|
||||
runbook.TextSnippets = deploymentDetails
|
||||
return runbook
|
||||
}
|
||||
|
||||
func (dph *DeploymentConfigHandler) HandleAction(e *socketmode.Event, alertName string) models.Runbook {
|
||||
var runbook models.Runbook
|
||||
configDiff := diagnostic.DeploymentConfigHandler(dph.deploymentConfigHandlerData.repository, alertName)
|
||||
runbook.TextSnippets = configDiff
|
||||
return runbook
|
||||
}
|
||||
@@ -2,16 +2,11 @@ package processor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/slack-go/slack"
|
||||
"github.com/slack-go/slack/socketmode"
|
||||
"go.uber.org/zap"
|
||||
"houston/internal/diagnostic"
|
||||
"houston/internal/diagnostic/models"
|
||||
"houston/internal/processor/action"
|
||||
"houston/logger"
|
||||
"houston/pkg/slackbot"
|
||||
rcaService "houston/service/rca/impl"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type CommandProcessor interface {
|
||||
@@ -23,24 +18,6 @@ type SlashCommandProcessor struct {
|
||||
slashCommandAction *action.HoustonCommandAction
|
||||
}
|
||||
|
||||
type DiagnosticCommandProcessor struct {
|
||||
logger *zap.Logger
|
||||
socketModeClient *socketmode.Client
|
||||
diagnosticCommandActions []action.DiagnosticCommandActionHandler
|
||||
}
|
||||
|
||||
func NewDiagnosticCommandProcessor(socketModeClient *socketmode.Client, repository *diagnostic.Repository) *DiagnosticCommandProcessor {
|
||||
return &DiagnosticCommandProcessor{
|
||||
socketModeClient: socketModeClient,
|
||||
diagnosticCommandActions: []action.DiagnosticCommandActionHandler{
|
||||
action.NewDeploymentConfigActionHandler(socketModeClient, repository),
|
||||
action.NewGrafanaActionHandler(socketModeClient, repository),
|
||||
//action.NewKibanaActionHandler(logger, socketModeClient),
|
||||
action.NewDeploymentPortalActionHandler(socketModeClient, repository),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func NewSlashCommandProcessor(
|
||||
socketModeClient *socketmode.Client,
|
||||
slackBot *slackbot.Client,
|
||||
@@ -61,50 +38,3 @@ func (scp *SlashCommandProcessor) ProcessSlashCommand(event *socketmode.Event) {
|
||||
|
||||
scp.slashCommandAction.PerformAction(event)
|
||||
}
|
||||
|
||||
func (dcp *DiagnosticCommandProcessor) ShouldProcessCommand(text string) bool {
|
||||
commands := strings.Split(text, " ")
|
||||
return len(commands) > 0 && commands[0] == "diagnose"
|
||||
}
|
||||
|
||||
func (dcp *DiagnosticCommandProcessor) ProcessSlashCommand(e *socketmode.Event) {
|
||||
dcp.socketModeClient.Ack(*e.Request, nil)
|
||||
|
||||
_, ok := e.Data.(slack.SlashCommand)
|
||||
if !ok {
|
||||
logger.Error("Unable to convert event to slash command", zap.Any("event", e))
|
||||
return
|
||||
}
|
||||
cmd := e.Data.(slack.SlashCommand)
|
||||
postAckMessage(&dcp.socketModeClient.Client, &cmd)
|
||||
commands := strings.Split(cmd.Text, " ")
|
||||
runbooks := make([]models.Runbook, 0)
|
||||
for _, actionHandler := range dcp.diagnosticCommandActions {
|
||||
runbooks = append(runbooks, actionHandler.HandleAction(e, commands[1]))
|
||||
}
|
||||
diagnosticDataAssembler := diagnostic.NewDiagnosticDataAssembler2()
|
||||
image, text := diagnosticDataAssembler.HandleData(runbooks)
|
||||
postDataOnSlack(&dcp.socketModeClient.Client, &cmd, &image, text)
|
||||
|
||||
logger.Error("S3 upload failed", zap.Any("error", e))
|
||||
}
|
||||
|
||||
func postDataOnSlack(api *slack.Client, sh *slack.SlashCommand, message *slack.MsgOption, attachments []slack.Attachment) {
|
||||
y, x, err := api.PostMessage(
|
||||
sh.ChannelID,
|
||||
*message,
|
||||
slack.MsgOptionAttachments(attachments...),
|
||||
)
|
||||
if err != nil {
|
||||
fmt.Printf("error occured %s", err)
|
||||
}
|
||||
fmt.Println(y, x)
|
||||
}
|
||||
|
||||
func postAckMessage(api *slack.Client, sh *slack.SlashCommand) {
|
||||
textBlock := slack.NewTextBlockObject("mrkdwn", fmt.Sprintf("Your request has been acknowledged for\n> %s", sh.Text), false, false)
|
||||
block := slack.NewSectionBlock(textBlock, nil, nil)
|
||||
var messageBlock slack.MsgOption
|
||||
messageBlock = slack.MsgOptionBlocks(block)
|
||||
postDataOnSlack(api, sh, &messageBlock, nil)
|
||||
}
|
||||
|
||||
@@ -14,23 +14,20 @@ import (
|
||||
)
|
||||
|
||||
type HoustonCommandResolver struct {
|
||||
diagnosticCommandProcessor *processor.DiagnosticCommandProcessor
|
||||
socketModeClient *socketmode.Client
|
||||
slackBotClient *slackbot.Client
|
||||
rcaService *rcaService.RcaService
|
||||
socketModeClient *socketmode.Client
|
||||
slackBotClient *slackbot.Client
|
||||
rcaService *rcaService.RcaService
|
||||
}
|
||||
|
||||
func NewHoustonCommandResolver(
|
||||
diagnosticCommandProcessor *processor.DiagnosticCommandProcessor,
|
||||
socketModeClient *socketmode.Client,
|
||||
slackBotClient *slackbot.Client,
|
||||
rcaService *rcaService.RcaService,
|
||||
) *HoustonCommandResolver {
|
||||
return &HoustonCommandResolver{
|
||||
diagnosticCommandProcessor: diagnosticCommandProcessor,
|
||||
socketModeClient: socketModeClient,
|
||||
slackBotClient: slackBotClient,
|
||||
rcaService: rcaService,
|
||||
socketModeClient: socketModeClient,
|
||||
slackBotClient: slackBotClient,
|
||||
rcaService: rcaService,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,9 +35,6 @@ const logTag = "[houston_command_resolver]"
|
||||
|
||||
func (resolver *HoustonCommandResolver) Resolve(evt *socketmode.Event) processor.CommandProcessor {
|
||||
cmd, _ := evt.Data.(slack.SlashCommand)
|
||||
if resolver.diagnosticCommandProcessor.ShouldProcessCommand(cmd.Text) {
|
||||
resolver.diagnosticCommandProcessor.ProcessSlashCommand(evt)
|
||||
}
|
||||
logger.Info(fmt.Sprintf("%s received slash command [%s] from channel [%s] by user [%s]", logTag, fmt.Sprintf("%s %s", cmd.Command, cmd.Text), cmd.ChannelName, cmd.UserName))
|
||||
|
||||
params := strings.ToLower(cmd.Text)
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
package role
|
||||
|
||||
import "gorm.io/gorm"
|
||||
|
||||
type RoleEntity struct {
|
||||
gorm.Model
|
||||
name string `gorm:"column:name"`
|
||||
}
|
||||
|
||||
func (RoleEntity) TableName() string {
|
||||
return "role"
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
package role
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"houston/logger"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type Repository struct {
|
||||
gormClient *gorm.DB
|
||||
}
|
||||
|
||||
func NewRoleRepository(gormClient *gorm.DB) *Repository {
|
||||
return &Repository{
|
||||
gormClient: gormClient,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Repository) FindRoleById(id int) (*RoleEntity, error) {
|
||||
role := RoleEntity{}
|
||||
tx := r.gormClient.Raw("select * from role where id = ?", id).Scan(&role)
|
||||
|
||||
if tx.Error != nil {
|
||||
logger.Error("Error while getting role", zap.String("exception", fmt.Sprintf("%v", tx.Error)))
|
||||
return nil, tx.Error
|
||||
}
|
||||
if tx.RowsAffected == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return &role, nil
|
||||
}
|
||||
Reference in New Issue
Block a user