# What this PR does
New OnCall plugin initialization process
## Checklist
- [x] Unit, integration, and e2e (if applicable) tests updated
- [x] Documentation added (or `pr:no public docs` PR label added if not
required)
- [x] Added the relevant release notes label (see labels prefixed w/
`release:`). These labels dictate how your PR will
show up in the autogenerated release notes.
---------
Co-authored-by: Michael Derynck <michael.derynck@grafana.com>
Co-authored-by: Matias Bordese <mbordese@gmail.com>
137 lines
3.8 KiB
Go
137 lines
3.8 KiB
Go
package plugin
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"net/http"
|
|
"sort"
|
|
|
|
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
|
|
)
|
|
|
|
type OnCallSync struct {
|
|
Users []OnCallUser `json:"users"`
|
|
Teams []OnCallTeam `json:"teams"`
|
|
TeamMembers map[int][]int `json:"team_members"`
|
|
Settings OnCallPluginSettings `json:"settings"`
|
|
}
|
|
|
|
func (a *OnCallSync) Equal(b *OnCallSync) bool {
|
|
if len(a.Users) != len(b.Users) || len(a.Teams) != len(b.Teams) || len(a.TeamMembers) != len(b.TeamMembers) {
|
|
return false
|
|
}
|
|
for i := range a.Users {
|
|
if !a.Users[i].Equal(&b.Users[i]) {
|
|
return false
|
|
}
|
|
}
|
|
for i := range a.Teams {
|
|
if !a.Teams[i].Equal(&b.Teams[i]) {
|
|
return false
|
|
}
|
|
}
|
|
for key, teamMembersA := range a.TeamMembers {
|
|
if teamMembersB, exists := b.TeamMembers[key]; !exists {
|
|
if len(teamMembersA) != len(teamMembersB) {
|
|
return false
|
|
}
|
|
sort.Slice(teamMembersA, func(i, j int) bool {
|
|
return teamMembersA[i] < teamMembersA[j]
|
|
})
|
|
sort.Slice(teamMembersB, func(i, j int) bool {
|
|
return teamMembersB[i] < teamMembersB[j]
|
|
})
|
|
for i := range teamMembersA {
|
|
if teamMembersA[i] != teamMembersB[i] {
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if !a.Settings.Equal(&b.Settings) {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
type responseWriter struct {
|
|
http.ResponseWriter
|
|
statusCode int
|
|
body bytes.Buffer
|
|
}
|
|
|
|
func (rw *responseWriter) WriteHeader(statusCode int) {
|
|
rw.statusCode = statusCode
|
|
rw.ResponseWriter.WriteHeader(statusCode)
|
|
}
|
|
|
|
func (rw *responseWriter) Write(b []byte) (int, error) {
|
|
if rw.statusCode == 0 {
|
|
rw.WriteHeader(http.StatusOK)
|
|
}
|
|
n, err := rw.body.Write(b)
|
|
if err != nil {
|
|
return n, err
|
|
}
|
|
return rw.ResponseWriter.Write(b)
|
|
}
|
|
|
|
func afterRequest(handler http.Handler, afterFunc func(*responseWriter, *http.Request)) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
wrappedWriter := &responseWriter{ResponseWriter: w}
|
|
handler.ServeHTTP(wrappedWriter, r)
|
|
afterFunc(wrappedWriter, r)
|
|
})
|
|
}
|
|
|
|
func (a *App) handleInternalApi(w http.ResponseWriter, req *http.Request) {
|
|
a.ProxyRequestToOnCall(w, req, "api/internal/v1/")
|
|
}
|
|
|
|
func (a *App) handleLegacyInstall(w *responseWriter, req *http.Request) {
|
|
var provisioningData OnCallProvisioningJSONData
|
|
err := json.Unmarshal(w.body.Bytes(), &provisioningData)
|
|
if err != nil {
|
|
log.DefaultLogger.Error("Error unmarshalling OnCallProvisioningJSONData", "error", err)
|
|
return
|
|
}
|
|
|
|
onCallPluginSettings, err := a.OnCallSettingsFromContext(req.Context())
|
|
if err != nil {
|
|
log.DefaultLogger.Error("Error getting settings from context", "error", err)
|
|
return
|
|
}
|
|
|
|
if provisioningData.Error != "" {
|
|
log.DefaultLogger.Error("Error installing OnCall", "error", provisioningData.Error)
|
|
return
|
|
}
|
|
onCallPluginSettings.License = provisioningData.License
|
|
onCallPluginSettings.OrgID = provisioningData.OrgId
|
|
onCallPluginSettings.StackID = provisioningData.StackId
|
|
onCallPluginSettings.OnCallToken = provisioningData.OnCallToken
|
|
|
|
err = a.SaveOnCallSettings(onCallPluginSettings)
|
|
if err != nil {
|
|
log.DefaultLogger.Error("Error saving settings", "error", err)
|
|
return
|
|
}
|
|
}
|
|
|
|
// registerRoutes takes a *http.ServeMux and registers some HTTP handlers.
|
|
func (a *App) registerRoutes(mux *http.ServeMux) {
|
|
mux.HandleFunc("/plugin/install", a.handleInstall)
|
|
mux.HandleFunc("/plugin/status", a.handleStatus)
|
|
mux.HandleFunc("/plugin/sync", a.handleSync)
|
|
|
|
mux.Handle("/plugin/self-hosted/install", afterRequest(http.HandlerFunc(a.handleInternalApi), a.handleLegacyInstall))
|
|
|
|
// Disable debug endpoints
|
|
//mux.HandleFunc("/debug/user", a.handleDebugUser)
|
|
//mux.HandleFunc("/debug/sync", a.handleDebugSync)
|
|
//mux.HandleFunc("/debug/settings", a.handleDebugSettings)
|
|
//mux.HandleFunc("/debug/permissions", a.handleDebugPermissions)
|
|
//mux.HandleFunc("/debug/stats", a.handleDebugStats)
|
|
|
|
mux.HandleFunc("/", a.handleInternalApi)
|
|
}
|