# 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>
138 lines
3.3 KiB
Go
138 lines
3.3 KiB
Go
package plugin
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
|
|
"net/http"
|
|
"net/url"
|
|
"strconv"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
type OnCallSyncCache struct {
|
|
syncMutex sync.Mutex
|
|
lastOnCallSync *OnCallSync
|
|
}
|
|
|
|
func (a *App) handleSync(w http.ResponseWriter, req *http.Request) {
|
|
if req.Method != http.MethodPost {
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
waitToCompleteParameter := req.URL.Query().Get("wait")
|
|
var waitToComplete = false
|
|
var err error
|
|
if waitToCompleteParameter != "" {
|
|
waitToComplete, err = strconv.ParseBool(waitToCompleteParameter)
|
|
if err != nil {
|
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
return
|
|
}
|
|
}
|
|
|
|
forceSendParameter := req.URL.Query().Get("force")
|
|
var forceSend = false
|
|
if forceSendParameter != "" {
|
|
forceSend, err = strconv.ParseBool(forceSendParameter)
|
|
if err != nil {
|
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
return
|
|
}
|
|
}
|
|
|
|
if waitToComplete {
|
|
err := a.makeSyncRequest(req.Context(), forceSend)
|
|
if err != nil {
|
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
return
|
|
}
|
|
} else {
|
|
go func() {
|
|
err := a.makeSyncRequest(req.Context(), forceSend)
|
|
if err != nil {
|
|
log.DefaultLogger.Error("Error making sync request", "error", err)
|
|
}
|
|
}()
|
|
}
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
}
|
|
|
|
func (a *App) compareSyncData(newOnCallSync *OnCallSync) bool {
|
|
if a.lastOnCallSync == nil {
|
|
log.DefaultLogger.Info("No saved OnCallSync to compare")
|
|
return false
|
|
}
|
|
return newOnCallSync.Equal(a.lastOnCallSync)
|
|
}
|
|
|
|
func (a *App) makeSyncRequest(ctx context.Context, forceSend bool) error {
|
|
startMakeSyncRequest := time.Now()
|
|
defer func() {
|
|
elapsed := time.Since(startMakeSyncRequest)
|
|
log.DefaultLogger.Info("makeSyncRequest", "time", elapsed.Milliseconds())
|
|
}()
|
|
|
|
locked := a.syncMutex.TryLock()
|
|
if !locked {
|
|
return errors.New("sync already in progress")
|
|
}
|
|
defer a.syncMutex.Unlock()
|
|
|
|
onCallPluginSettings, err := a.OnCallSettingsFromContext(ctx)
|
|
if err != nil {
|
|
return fmt.Errorf("error getting settings from context: %v ", err)
|
|
}
|
|
|
|
onCallSync, err := a.GetSyncData(ctx, onCallPluginSettings)
|
|
if err != nil {
|
|
return fmt.Errorf("error getting sync data: %v", err)
|
|
}
|
|
|
|
same := a.compareSyncData(onCallSync)
|
|
if same && !forceSend {
|
|
log.DefaultLogger.Info("No changes detected to sync")
|
|
return nil
|
|
}
|
|
|
|
onCallSyncJsonData, err := json.Marshal(onCallSync)
|
|
if err != nil {
|
|
return fmt.Errorf("error marshalling JSON: %v", err)
|
|
}
|
|
|
|
syncURL, err := url.JoinPath(onCallPluginSettings.OnCallAPIURL, "api/internal/v1/plugin/v2/sync")
|
|
if err != nil {
|
|
return fmt.Errorf("error joining path: %v", err)
|
|
}
|
|
|
|
parsedSyncURL, err := url.Parse(syncURL)
|
|
if err != nil {
|
|
return fmt.Errorf("error parsing path: %v", err)
|
|
}
|
|
|
|
syncReq, err := http.NewRequest("POST", parsedSyncURL.String(), bytes.NewBuffer(onCallSyncJsonData))
|
|
if err != nil {
|
|
return fmt.Errorf("error creating request: %v", err)
|
|
}
|
|
|
|
err = a.SetupRequestHeadersForOnCall(ctx, onCallPluginSettings, syncReq)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
syncReq.Header.Set("Content-Type", "application/json")
|
|
|
|
res, err := a.httpClient.Do(syncReq)
|
|
if err != nil {
|
|
return fmt.Errorf("error request to oncall: %v", err)
|
|
}
|
|
defer res.Body.Close()
|
|
|
|
a.lastOnCallSync = onCallSync
|
|
return nil
|
|
}
|