Skip to content

Commit

Permalink
[IRM] - changes to get incident working in IRM plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
joeyorlando committed Sep 4, 2024
1 parent 0fa3522 commit d424c50
Show file tree
Hide file tree
Showing 12 changed files with 124 additions and 97 deletions.
7 changes: 1 addition & 6 deletions grafana-plugin/.bra.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,14 @@
[run]
init_cmds = [
["mage", "-v", "build:debug"],
["mage", "-v" , "reloadPlugin"],
]
watch_all = true
follow_symlinks = false
ignore = [".git", "node_modules", "dist"]
ignore_files = ["mage_output_file.go"]
watch_dirs = [
"pkg",
# "src",
]
watch_dirs = ["pkg"]
watch_exts = [".go", ".json"]
build_delay = 2000
cmds = [
["mage", "-v", "build:debug"],
["mage", "-v" , "reloadPlugin"],
]
2 changes: 1 addition & 1 deletion grafana-plugin/go.mod
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module github.com/grafana-labs/grafana-oncall-app
module github.com/grafana/grafana-oncall-app

go 1.21.5

Expand Down
4 changes: 2 additions & 2 deletions grafana-plugin/pkg/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package main
import (
"os"

"github.com/grafana-labs/grafana-oncall-app/pkg/plugin"
"github.com/grafana/grafana-oncall-app/pkg/plugin"
"github.com/grafana/grafana-plugin-sdk-go/backend/app"
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
)
Expand All @@ -16,7 +16,7 @@ func main() {
// argument. This factory will be automatically called on incoming request
// from Grafana to create different instances of `App` (per plugin
// ID).
if err := app.Manage("grafana-oncall-app", plugin.NewApp, app.ManageOpts{}); err != nil {
if err := app.Manage("grafana-oncall-app", plugin.NewInstance, app.ManageOpts{}); err != nil {
log.DefaultLogger.Error(err.Error())
os.Exit(1)
}
Expand Down
27 changes: 20 additions & 7 deletions grafana-plugin/pkg/plugin/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,9 @@ type App struct {
}

// NewApp creates a new example *App instance.
func NewApp(ctx context.Context, settings backend.AppInstanceSettings) (instancemgmt.Instance, error) {
func NewApp(ctx context.Context, settings backend.AppInstanceSettings) (*App, error) {
var app App

// Use a httpadapter (provided by the SDK) for resource calls. This allows us
// to use a *http.ServeMux for resource calls, so we can map multiple routes
// to CallResource without having to implement extra logic.
mux := http.NewServeMux()
app.registerRoutes(mux)
app.CallResourceHandler = httpadapter.New(mux)
app.OnCallSyncCache = &OnCallSyncCache{}
app.OnCallSettingsCache = &OnCallSettingsCache{}
app.OnCallUserCache = NewOnCallUserCache()
Expand All @@ -66,6 +60,25 @@ func NewApp(ctx context.Context, settings backend.AppInstanceSettings) (instance
return &app, nil
}

// NewInstance creates a new example *Instance instance.
func NewInstance(ctx context.Context, settings backend.AppInstanceSettings) (instancemgmt.Instance, error) {
app, err := NewApp(ctx, settings)

if err != nil {
log.DefaultLogger.Error("Error creating new app", "error", err)
return nil, err
}

// Use a httpadapter (provided by the SDK) for resource calls. This allows us
// to use a *http.ServeMux for resource calls, so we can map multiple routes
// to CallResource without having to implement extra logic.
mux := http.NewServeMux()
app.registerRoutes(mux)
app.CallResourceHandler = httpadapter.New(mux)

return app, nil
}

// Dispose here tells plugin SDK that plugin wants to clean up resources when a new instance
// created.
func (a *App) Dispose() {
Expand Down
5 changes: 3 additions & 2 deletions grafana-plugin/pkg/plugin/debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ package plugin

import (
"encoding/json"
"net/http"

"github.com/grafana/grafana-plugin-sdk-go/backend/log"
"github.com/grafana/grafana-plugin-sdk-go/backend/resource/httpadapter"
"net/http"
)

type OnCallDebugStats struct {
Expand Down Expand Up @@ -47,7 +48,7 @@ func (a *App) handleDebugSync(w http.ResponseWriter, req *http.Request) {
return
}

onCallSync, err := a.GetSyncData(req.Context(), onCallPluginSettings)
onCallSync, err := a.GetSyncData(onCallPluginSettings)
if err != nil {
log.DefaultLogger.Error("Error getting sync data", "error", err)
return
Expand Down
109 changes: 60 additions & 49 deletions grafana-plugin/pkg/plugin/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,76 +15,55 @@ type OnCallInstall struct {
OnCallError `json:"onCallError,omitempty"`
}

func (a *App) handleInstall(w http.ResponseWriter, req *http.Request) {
if req.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}

locked := a.installMutex.TryLock()
if !locked {
http.Error(w, "Install is already in progress", http.StatusBadRequest)
return
}
defer a.installMutex.Unlock()
func (a *App) InstallOnCallFromPluginSettings(pluginSettings *OnCallPluginSettings, persistPluginSettingsToGrafana bool) (*OnCallProvisioningJSONData, error, int) {
var provisioningData OnCallProvisioningJSONData

onCallPluginSettings, err := a.OnCallSettingsFromContext(req.Context())
if err != nil {
log.DefaultLogger.Error("Error getting settings from context", "error", err)
return
}

healthStatus, err := a.CheckOnCallApiHealthStatus(onCallPluginSettings)
healthStatus, err := a.CheckOnCallApiHealthStatus(pluginSettings)
if err != nil {
log.DefaultLogger.Error("Error checking on-call API health", "error", err)
http.Error(w, err.Error(), healthStatus)
return
return nil, err, healthStatus
}

onCallSync, err := a.GetSyncData(req.Context(), onCallPluginSettings)
onCallSync, err := a.GetSyncData(pluginSettings)
if err != nil {
log.DefaultLogger.Error("Error getting sync data", "error", err)
return
return nil, err, http.StatusInternalServerError
}

onCallSyncJsonData, err := json.Marshal(onCallSync)
if err != nil {
log.DefaultLogger.Error("Error marshalling JSON", "error", err)
return
return nil, err, http.StatusInternalServerError
}

installURL, err := url.JoinPath(onCallPluginSettings.OnCallAPIURL, "api/internal/v1/plugin/v2/install")
installURL, err := url.JoinPath(pluginSettings.OnCallAPIURL, "api/internal/v1/plugin/v2/install")
if err != nil {
log.DefaultLogger.Error("Error joining path", "error", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
return nil, err, http.StatusInternalServerError
}

parsedInstallURL, err := url.Parse(installURL)
if err != nil {
log.DefaultLogger.Error("Error parsing path", "error", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
return nil, err, http.StatusInternalServerError
}

installReq, err := http.NewRequest("POST", parsedInstallURL.String(), bytes.NewBuffer(onCallSyncJsonData))
if err != nil {
log.DefaultLogger.Error("Error creating request", "error", err)
http.Error(w, err.Error(), http.StatusBadRequest)
return
return nil, err, http.StatusBadRequest
}
installReq.Header.Set("Content-Type", "application/json")

res, err := a.httpClient.Do(installReq)
if err != nil {
log.DefaultLogger.Error("Error request to oncall", "error", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
return nil, err, http.StatusInternalServerError
}
defer res.Body.Close()

if res.StatusCode != http.StatusOK {
errorBody, err := io.ReadAll(res.Body)
errorBody, _ := io.ReadAll(res.Body)
var installError = OnCallInstall{
OnCallError: OnCallError{
Code: INSTALL_ERROR_CODE,
Expand All @@ -104,33 +83,65 @@ func (a *App) handleInstall(w http.ResponseWriter, req *http.Request) {
}
}

w.Header().Add("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(installError); err != nil {
http.Error(w, "Failed to encode response", http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusBadRequest)
// TODO: what to do here?
// w.Header().Add("Content-Type", "application/json")
// if err := json.NewEncoder(w).Encode(installError); err != nil {
// log.DefaultLogger.Error("Error encoding response", "error", err)
// http.Error(w, "Failed to encode response", http.StatusInternalServerError)
// return err, http.StatusInternalServerError
// }
// w.WriteHeader(http.StatusBadRequest)
} else {
provisionBody, err := io.ReadAll(res.Body)
if err != nil {
log.DefaultLogger.Error("Error reading response body", "error", err)
return
return nil, err, http.StatusInternalServerError
}

var provisioningData OnCallProvisioningJSONData
err = json.Unmarshal(provisionBody, &provisioningData)
if err != nil {
log.DefaultLogger.Error("Error unmarshalling OnCallProvisioningJSONData", "error", err)
return
return nil, err, http.StatusInternalServerError
}

onCallPluginSettings.OnCallToken = provisioningData.OnCallToken
err = a.SaveOnCallSettings(onCallPluginSettings)
if err != nil {
log.DefaultLogger.Error("Error saving settings", "error", err)
return
if persistPluginSettingsToGrafana {
pluginSettings.OnCallToken = provisioningData.OnCallToken

if err = a.SaveOnCallSettings(pluginSettings); err != nil {
log.DefaultLogger.Error("Error saving settings", "error", err)
return nil, err, http.StatusInternalServerError
}
}
w.WriteHeader(http.StatusOK)
}

return &provisioningData, nil, http.StatusOK
}

func (a *App) handleInstall(w http.ResponseWriter, req *http.Request) {
if req.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}

locked := a.installMutex.TryLock()
if !locked {
http.Error(w, "Install is already in progress", http.StatusBadRequest)
return
}
defer a.installMutex.Unlock()

onCallPluginSettings, err := a.OnCallSettingsFromContext(req.Context())
if err != nil {
log.DefaultLogger.Error("Error getting settings from context", "error", err)
return
}

_, err, httpStatusCode := a.InstallOnCallFromPluginSettings(onCallPluginSettings, true)
if err != nil {
log.DefaultLogger.Error("Error installing oncall", "error", err)
http.Error(w, err.Error(), httpStatusCode)
return
}

w.WriteHeader(http.StatusOK)
}
10 changes: 5 additions & 5 deletions grafana-plugin/pkg/plugin/resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ func afterRequest(handler http.Handler, afterFunc func(*responseWriter, *http.Re
})
}

func (a *App) handleInternalApi(w http.ResponseWriter, req *http.Request) {
func (a *App) HandleInternalApi(w http.ResponseWriter, req *http.Request) {
a.ProxyRequestToOnCall(w, req, "api/internal/v1/")
}

Expand Down Expand Up @@ -121,10 +121,10 @@ func (a *App) handleLegacyInstall(w *responseWriter, req *http.Request) {
// 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.HandleFunc("/plugin/status", a.HandleStatus)
mux.HandleFunc("/plugin/sync", a.HandleSync)

mux.Handle("/plugin/self-hosted/install", afterRequest(http.HandlerFunc(a.handleInternalApi), a.handleLegacyInstall))
mux.Handle("/plugin/self-hosted/install", afterRequest(http.HandlerFunc(a.HandleInternalApi), a.handleLegacyInstall))

// Disable debug endpoints
//mux.HandleFunc("/debug/user", a.handleDebugUser)
Expand All @@ -134,5 +134,5 @@ func (a *App) registerRoutes(mux *http.ServeMux) {
//mux.HandleFunc("/debug/stats", a.handleDebugStats)
//mux.HandleFunc("/debug/unlock", a.handleDebugUnlock)

mux.HandleFunc("/", a.handleInternalApi)
mux.HandleFunc("/", a.HandleInternalApi)
}
5 changes: 3 additions & 2 deletions grafana-plugin/pkg/plugin/resources_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ package plugin
import (
"bytes"
"context"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"testing"

"github.com/grafana/grafana-plugin-sdk-go/backend"
)

// mockCallResourceResponseSender implements backend.CallResourceResponseSender
Expand All @@ -23,7 +24,7 @@ func (s *mockCallResourceResponseSender) Send(response *backend.CallResourceResp
// This ensures the httpadapter for CallResource works correctly.
func TestCallResource(t *testing.T) {
// Initialize app
inst, err := NewApp(context.Background(), backend.AppInstanceSettings{})
inst, err := NewInstance(context.Background(), backend.AppInstanceSettings{})
if err != nil {
t.Fatalf("new app: %s", err)
}
Expand Down
17 changes: 7 additions & 10 deletions grafana-plugin/pkg/plugin/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -317,32 +317,29 @@ func (a *App) SaveOnCallSettings(settings *OnCallPluginSettings) error {
return nil
}

func (a *App) GetSyncData(ctx context.Context, settings *OnCallPluginSettings) (*OnCallSync, error) {
func (a *App) GetSyncData(pluginSettings *OnCallPluginSettings) (*OnCallSync, error) {
var err error

startGetSyncData := time.Now()
defer func() {
elapsed := time.Since(startGetSyncData)
log.DefaultLogger.Info("GetSyncData", "time", elapsed.Milliseconds())
}()

onCallPluginSettings, err := a.OnCallSettingsFromContext(ctx)
if err != nil {
return nil, fmt.Errorf("error getting settings from context = %v", err)
}

onCallSync := OnCallSync{
Settings: *settings,
Settings: *pluginSettings,
}
onCallSync.Users, err = a.GetAllUsersWithPermissions(onCallPluginSettings)
onCallSync.Users, err = a.GetAllUsersWithPermissions(pluginSettings)
if err != nil {
return nil, fmt.Errorf("error getting users = %v", err)
}

onCallSync.Teams, err = a.GetAllTeams(onCallPluginSettings)
onCallSync.Teams, err = a.GetAllTeams(pluginSettings)
if err != nil {
return nil, fmt.Errorf("error getting teams = %v", err)
}

teamMembers, err := a.GetAllTeamMembers(onCallPluginSettings, onCallSync.Teams)
teamMembers, err := a.GetAllTeamMembers(pluginSettings, onCallSync.Teams)
if err != nil {
return nil, fmt.Errorf("error getting team members = %v", err)
}
Expand Down
2 changes: 1 addition & 1 deletion grafana-plugin/pkg/plugin/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ func (a *App) ValidateOnCallStatus(ctx context.Context, settings *OnCallPluginSe
return &status, nil
}

func (a *App) handleStatus(w http.ResponseWriter, req *http.Request) {
func (a *App) HandleStatus(w http.ResponseWriter, req *http.Request) {
if req.Method != http.MethodGet {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
Expand Down
Loading

0 comments on commit d424c50

Please sign in to comment.