HealthPartnersOSS/snogo

View on GitHub
httpservice.go

Summary

Maintainability
A
1 hr
Test Coverage
package snogo

import (
    "encoding/json"
    "flag"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "strings"

    "github.com/prometheus/client_golang/prometheus/promhttp"
)

var (
    flagPort = flag.String("port", "8080", "Listening Port")
    snClient = DefaultServiceNowClient()
)

func init() {
    log.SetFlags(log.Lmicroseconds | log.Lshortfile)
    flag.Parse()
}

type prometheusAlertPayload struct {
    Version     string `json:"version"`
    GroupKey    string `json:"groupKey"`
    Status      string `json:"status"`
    Receiver    string `json:"receiver"`
    ExternalURL string `json:"externalURL"`
    Alerts      []struct {
        Status string `json:"status"`
        Labels struct {
            SnowGroup        string `json:"snow_group"`
            OpenShiftCluster string `json:"openshift_cluster"`
        } `json:"labels"`
        Annotations struct {
            Description      string `json:"description"`
            ShortDescription string `json:"summary"`
            RunBook          string `json:"runbook"`
        } `json:"annotations"`
    } `json:"alerts"`
}

func StartServer() {
    mux := http.NewServeMux()
    mux.HandleFunc("/", postHandler)
    mux.Handle("/metrics", promhttp.Handler())
    log.Printf("listening on port %s", *flagPort)
    log.Fatal(http.ListenAndServe("localhost:"+*flagPort, mux))
}

func postHandler(w http.ResponseWriter, r *http.Request) {
    if r.Method == "POST" {
        bytesReturned, err := ioutil.ReadAll(r.Body)
        if err != nil {
            http.Error(w, "Error processing request body",
                http.StatusBadRequest)
        }

        // It would be better to check if the body is valid JSON
        body := string(bytesReturned)
        if body == "" {
            http.Error(w, "Request must contain a body",
                http.StatusBadRequest)
        }

        prometheusPayload, err := serializeJSON(body)
        if err != nil {
            http.Error(w, "Unable to deserialize JSON", http.StatusInternalServerError)
        } else {
            fmt.Printf("%+v\n", prometheusPayload)
            fmt.Fprint(w, "Deserialized JSON successfully!")
        }

        // TODO: Why are we ignoring errors here? I assume because the input is 'validated'
        // per the serializeJSON func? If so, let's just kill error checking here altogether.
        incidentsToCreate, _ := transform(&prometheusPayload)

        for _, incident := range incidentsToCreate {
            if len(incident.AssignmentGroup) > 0 {
                postBody, err := json.Marshal(incident)
                if err != nil {
                    fmt.Println(err)
                } else {
                    _, err := snClient.Create("incident", postBody)
                    if err != nil {
                        // Do a thing
                    }
                }
            } else {
                fmt.Printf("No assignment group for incident %s, not created\n", incident.Description)
            }
        }
    } else {
        http.Error(w, "Invalid request method", http.StatusMethodNotAllowed)
    }
}

func serializeJSON(j string) (prometheusAlertPayload, error) {
    // Prepare JSON as bytecode
    b := []byte(j)

    // Unmarshal into struct
    var m prometheusAlertPayload
    err := json.Unmarshal(b, &m)

    return m, err
}

func transform(payload *prometheusAlertPayload) ([]IncidentCreationPayload, error) {
    var incidentList []IncidentCreationPayload
    for _, alert := range payload.Alerts {
        incident := IncidentCreationPayload{
            AssignmentGroup:  strings.Replace(alert.Labels.SnowGroup, "-", " ", -1),
            ContactType:      "Auto Ticket",
            Customer:         "sn_web_api",
            Description:      alert.Annotations.Description,
            Impact:           "4",
            ShortDescription: alert.Annotations.ShortDescription,
            State:            json.Number("60"),
            Urgency:          "3",
        }
        incidentList = append(incidentList, incident)
    }
    return incidentList, nil
}