TP-0000 | Metric setup And On Call Add And Fkey (#18)

* TP-0000 | Metric setup

* TP-0000 | adding oncall

* TP-0000 | fkey
This commit is contained in:
Abhijeet Gupta
2023-04-19 20:26:37 +05:30
committed by GitHub Enterprise
parent 96ce2fff26
commit 60ff45be60
9 changed files with 181 additions and 8 deletions

View File

@@ -3,6 +3,12 @@ package app
import (
"fmt"
"houston/cmd/app/handler"
"net/http"
"strconv"
"strings"
"time"
"houston/internal/metrics"
"github.com/gin-gonic/gin"
"github.com/spf13/viper"
@@ -27,11 +33,55 @@ func NewServer(gin *gin.Engine, logger *zap.Logger, db *gorm.DB) *Server {
func (s *Server) Handler() {
s.teamHandler()
s.severityHandler()
s.gin.Use(s.createMiddleware())
metrics.AdminHandler()
//this should always be at the end since it opens websocket to connect to slackbot
s.houstonHandler()
}
func (s *Server) createMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
whitelistedDomains := getWhitelistedDomains()
//cors handling
c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT")
c.Writer.Header().Set("Access-Control-Allow-Headers", viper.GetString("allowed.custom.headers"))
origin := c.Request.Header.Get("Origin")
if !whitelistedDomains[origin] {
c.AbortWithStatus(http.StatusUnauthorized)
return
}
c.Writer.Header().Set("Access-Control-Allow-Origin", origin)
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(http.StatusOK)
return
}
startTime := time.Now()
c.Next()
endTime := float64(time.Since(startTime))
//metrics publishing
status := strconv.Itoa(c.Writer.Status())
metrics.HoustonApiRequestCounter.WithLabelValues(c.Request.URL.Path, c.Request.Method, status).Inc()
metrics.HoustonApiRequestLatencySum.WithLabelValues(c.Request.URL.Path, c.Request.Method, status).Add(endTime)
metrics.HoustonApiRequestLatencyHistogram.WithLabelValues(c.Request.URL.Path, c.Request.Method, status).Observe(endTime)
metrics.HoustonApiRequestLatencySummary.WithLabelValues(c.Request.URL.Path, c.Request.Method, status).Observe(endTime)
}
}
func getWhitelistedDomains() map[string]bool {
allowedList := make(map[string]bool)
domains := strings.Split(viper.GetString("whitelisted.domains"), ",")
for _, domain := range domains {
domainLocal := domain
allowedList[domainLocal] = true
}
return allowedList
}
func (s *Server) houstonHandler() {
houstonClient := NewHoustonClient(s.logger)
houstonHandler := handler.NewSlackHandler(s.logger, s.db, houstonClient.socketModeClient)

View File

@@ -1,6 +1,6 @@
env=ENV
port=PORT
metric.port=METRIC_POST
metrics.port=METRIC_POST
#houston token config
houston.slack.app.token=HOUSTON_SLACK_APP_TOKEN
@@ -22,4 +22,11 @@ cron.job.name=CRON_JOB_NAME
#incidents
incidents.show.limit=INCIDENTS_SHOW_LIMIT
# Prometheus Config
prometheus.app.namePROMETHEUS_APP_NAME
prometheus.host:PROMETHEUS_HOST
prometheus.port:PROMETHEUS_PORT
prometheus.enabled:PROMETHEUS_ENABLED
prometheus.timeout:PROMETHEUS_TIMEOUT
prometheus.flush.interval.in.ms:PROMETHEUS_FLUSH_INTERVAL_IN_MS
prometheus.histogram.buckets:PROMETHEUS_HISTOGRAM_BUCKETS

View File

@@ -44,7 +44,7 @@ create table team_tag
optional boolean default false,
created_at timestamp without time zone,
updated_at timestamp without time zone,
deleted_at timestamp without time zone
deleted_at timestamp without time zone
);
CREATE TABLE houston_user
@@ -157,4 +157,26 @@ CREATE TABLE shedlock
unlocked_by VARCHAR(64),
locked_value boolean DEFAULT true,
PRIMARY KEY (name)
);
);
ALTER TABLE incident ADD CONSTRAINT fk_status FOREIGN KEY(status) REFERENCES incident_status(id);
ALTER TABLE incident ADD CONSTRAINT fk_severity FOREIGN KEY(severity_id) REFERENCES severity(id);
ALTER TABLE incident ADD CONSTRAINT fk_team FOREIGN KEY(team_id) REFERENCES team(id);
ALTER TABLE team_tag ADD CONSTRAINT fk_team FOREIGN KEY(team_id) REFERENCES team(id);
ALTER TABLE team_tag ADD CONSTRAINT fk_tag FOREIGN KEY(tag_id) REFERENCES tag(id);
ALTER TABLE tag_value ADD CONSTRAINT fk_tag FOREIGN KEY(tag_id) REFERENCES tag(id);
ALTER TABLE incident_role ADD CONSTRAINT fk_incident FOREIGN KEY(incident_id) REFERENCES incident(id);
ALTER TABLE incident_channel ADD CONSTRAINT fk_incident FOREIGN KEY(incident_id) REFERENCES incident(id);
ALTER TABLE incident_tag ADD CONSTRAINT fk_incident FOREIGN KEY(incident_id) REFERENCES incident(id);
ALTER TABLE incident_tag ADD CONSTRAINT fk_tag FOREIGN KEY(tag_id) REFERENCES tag(id);

15
go.mod
View File

@@ -15,6 +15,16 @@ require (
gorm.io/gorm v1.24.5
)
require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.42.0 // indirect
github.com/prometheus/procfs v0.9.0 // indirect
)
require (
github.com/bytedance/sonic v1.8.2 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
@@ -41,6 +51,7 @@ require (
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
github.com/prometheus/client_golang v1.15.0
github.com/robfig/cron v1.2.0
github.com/spf13/afero v1.9.4 // indirect
github.com/spf13/cast v1.5.0 // indirect
@@ -57,9 +68,9 @@ require (
golang.org/x/arch v0.2.0 // indirect
golang.org/x/crypto v0.6.0 // indirect
golang.org/x/net v0.7.0 // indirect
golang.org/x/sys v0.5.0 // indirect
golang.org/x/sys v0.6.0 // indirect
golang.org/x/text v0.7.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect
google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

View File

@@ -0,0 +1,43 @@
package metrics
import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
)
var HoustonApiRequestCounter = promauto.NewCounterVec(
prometheus.CounterOpts{
Name: "houston_api_request_total",
Help: "api request counter",
},
[]string{"url", "method", "response_code"},
)
var HoustonApiRequestLatencySum = promauto.NewCounterVec(
prometheus.CounterOpts{
Name: "houston_api_request_latency_sum",
Help: "api request latency sum",
},
[]string{"url", "method", "response_code"},
)
var HoustonApiRequestLatencyHistogram = promauto.NewHistogramVec(
prometheus.HistogramOpts{
Name: "houston_api_request_latency_histogram",
Help: "api latency histogram",
},
[]string{"url", "method", "response_code"},
)
var HoustonApiRequestLatencySummary = promauto.NewSummaryVec(
prometheus.SummaryOpts{
Name: "houston_api_request_latency_summary",
Help: "api latency summary",
Objectives: map[float64]float64{
0.5: 0.1,
0.90: 0.05,
0.95: 0.05,
},
},
[]string{"url", "method", "response_code"},
)

View File

@@ -0,0 +1,17 @@
package metrics
import (
"fmt"
"github.com/gin-gonic/gin"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/spf13/viper"
)
func AdminHandler() {
ginServer := gin.New()
ginServer.GET("/metrics", gin.WrapH(promhttp.Handler()))
go func() {
ginServer.Run(fmt.Sprintf(":%v", viper.GetString("metrics.port")))
}()
}

View File

@@ -93,6 +93,7 @@ func (itp *IncidentUpdateTypeAction) IncidentUpdateType(callback slack.Interacti
}
itp.addDefaultUsersToIncident(callback.View.PrivateMetadata, incidentEntity.TeamId)
itp.tagOncallToIncident(int(incidentEntity.TeamId), callback.View.PrivateMetadata)
severityEntity, err := itp.severityService.FindSeverityById(incidentEntity.SeverityId)
if err != nil {
@@ -141,6 +142,16 @@ func (itp *IncidentUpdateTypeAction) addDefaultUsersToIncident(channelId string,
return nil
}
func (itp *IncidentUpdateTypeAction) tagOncallToIncident(teamId int, channelId string) {
teamEntity, err := itp.teamService.FindTeamById(uint(teamId))
if err != nil {
itp.logger.Error("error in fetching team err: %v", zap.Error(err))
}
msgOption := slack.MsgOptionText(fmt.Sprintf("<@%s>", teamEntity.OncallHandle), false)
_, ts, _ := itp.socketModeClient.PostMessage(channelId, msgOption)
itp.InviteOnCallPersonToIncident(channelId, ts)
}
func (itp *IncidentUpdateTypeAction) InviteOnCallPersonToIncident(channelId, ts string) {
go func() {
time.Sleep(3 * time.Second)

View File

@@ -77,6 +77,7 @@ func (cip *CreateIncidentAction) CreateIncidentModalCommandProcessing(callback s
return
}
cip.tagOncallToIncident(int(incidentEntity.TeamId), *channelID)
cip.InviteOnCallPersonToIncident(*channelID, *timestamp)
// Acknowledge the interaction callback
@@ -84,9 +85,19 @@ func (cip *CreateIncidentAction) CreateIncidentModalCommandProcessing(callback s
cip.client.Ack(*request, payload)
}
func (cip *CreateIncidentAction) tagOncallToIncident(teamId int, channelId string) {
teamEntity, err := cip.teamService.FindTeamById(uint(teamId))
if err != nil {
cip.logger.Error("error in fetching team err: %v", zap.Error(err))
}
msgOption := slack.MsgOptionText(fmt.Sprintf("<@%s>", teamEntity.OncallHandle), false)
_, ts, _ := cip.client.PostMessage(channelId, msgOption)
cip.InviteOnCallPersonToIncident(channelId, ts)
}
func (cip *CreateIncidentAction) InviteOnCallPersonToIncident(channelId, ts string) {
go func() {
time.Sleep(3 * time.Second)
time.Sleep(20 * time.Second)
msg, _, _, _ := cip.client.GetConversationReplies(&slack.GetConversationRepliesParameters{
ChannelID: channelId,
Timestamp: ts,

View File

@@ -10,6 +10,7 @@ type TeamEntity struct {
Name string `gorm:"column:name"`
SlackUserIds pq.StringArray `gorm:"column:slack_user_ids;type:string[]"`
ConfluenceLink string `gorm:"column:confluence_link"`
OncallHandle string `gorm:"column:oncall_handle"`
Active bool `gorm:"column:active"`
}