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:
committed by
GitHub Enterprise
parent
96ce2fff26
commit
60ff45be60
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
15
go.mod
@@ -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
|
||||
)
|
||||
|
||||
43
internal/metrics/metrics.go
Normal file
43
internal/metrics/metrics.go
Normal 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"},
|
||||
)
|
||||
17
internal/metrics/server.go
Normal file
17
internal/metrics/server.go
Normal 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")))
|
||||
}()
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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"`
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user