cloudfoundry-incubator/eirini

View on GitHub
handler/app_handler.go

Summary

Maintainability
A
0 mins
Test Coverage
B
85%
package handler

import (
    "bytes"
    "encoding/json"
    "errors"
    "net/http"
    "strconv"

    "code.cloudfoundry.org/eirini"
    "code.cloudfoundry.org/eirini/api"
    "code.cloudfoundry.org/eirini/models/cf"
    "code.cloudfoundry.org/lager"
    "github.com/julienschmidt/httprouter"
)

func NewAppHandler(lrpBifrost LRPBifrost, logger lager.Logger) *App {
    return &App{lrpBifrost: lrpBifrost, logger: logger}
}

type App struct {
    lrpBifrost LRPBifrost
    logger     lager.Logger
}

func (a *App) Desire(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
    loggerSession := a.logger.Session("desire-app", lager.Data{"guid": ps.ByName("process_guid")})

    var request cf.DesireLRPRequest

    buf := new(bytes.Buffer)
    if _, err := buf.ReadFrom(r.Body); err != nil {
        loggerSession.Error("request-body-cannot-be-read", err)
        w.WriteHeader(http.StatusBadRequest)

        return
    }

    if err := json.Unmarshal(buf.Bytes(), &request); err != nil {
        loggerSession.Error("request-body-decoding-failed", err)
        w.WriteHeader(http.StatusBadRequest)

        return
    }

    loggerSession.Debug("requested", lager.Data{"app_guid": request.AppGUID, "version": request.Version})

    request.LRP = buf.String()

    if err := a.lrpBifrost.Transfer(r.Context(), request); err != nil {
        loggerSession.Error("bifrost-failed", err)
        w.WriteHeader(http.StatusBadRequest)

        return
    }

    w.WriteHeader(http.StatusAccepted)
}

func (a *App) List(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
    loggerSession := a.logger.Session("list-apps")
    loggerSession.Debug("requested")

    desiredLRPSchedulingInfos, err := a.lrpBifrost.List(r.Context())
    if err != nil {
        loggerSession.Error("bifrost-failed", err)
        w.WriteHeader(http.StatusInternalServerError)

        return
    }

    response := cf.DesiredLRPSchedulingInfosResponse{
        DesiredLrpSchedulingInfos: desiredLRPSchedulingInfos,
    }

    w.Header().Set("Content-Type", "application/json")

    result, err := json.Marshal(&response)
    if err != nil {
        loggerSession.Error("encode-json-failed", err)
        w.WriteHeader(http.StatusInternalServerError)

        return
    }

    if _, err = w.Write(result); err != nil {
        loggerSession.Error("failed-to-write-response", err)
    }
}

func (a *App) Get(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
    loggerSession := a.logger.Session("get-app", lager.Data{"guid": ps.ByName("process_guid"), "version": ps.ByName("version_guid")})
    loggerSession.Debug("requested")

    identifier := api.LRPIdentifier{
        GUID:    ps.ByName("process_guid"),
        Version: ps.ByName("version_guid"),
    }

    desiredLRP, err := a.lrpBifrost.GetApp(r.Context(), identifier)
    if err != nil {
        if errors.Is(err, eirini.ErrNotFound) {
            loggerSession.Info("app-not-found")
            w.WriteHeader(http.StatusNotFound)

            return
        }

        loggerSession.Error("failed-to-get-lrp", err, lager.Data{"guid": identifier.GUID})
        w.WriteHeader(http.StatusInternalServerError)

        return
    }

    response := cf.DesiredLRPResponse{
        DesiredLRP: desiredLRP,
    }

    if err := json.NewEncoder(w).Encode(response); err != nil {
        loggerSession.Error("encode-json-failed", err)
        w.WriteHeader(http.StatusInternalServerError)

        return
    }
}

func (a *App) GetInstances(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
    loggerSession := a.logger.Session("get-app-instances", lager.Data{"guid": ps.ByName("process_guid"), "version": ps.ByName("version_guid")})
    loggerSession.Debug("requested")

    identifier := api.LRPIdentifier{
        GUID:    ps.ByName("process_guid"),
        Version: ps.ByName("version_guid"),
    }
    response := cf.GetInstancesResponse{ProcessGUID: identifier.ProcessGUID()}
    instances, err := a.lrpBifrost.GetInstances(r.Context(), identifier)
    response.Instances = instances

    if err != nil {
        loggerSession.Error("bifrost-failed", err)
        response.Error = err.Error()
        response.Instances = []*cf.Instance{}
    }

    if errors.Is(err, eirini.ErrNotFound) {
        w.WriteHeader(http.StatusNotFound)
    }

    err = json.NewEncoder(w).Encode(response)
    if err != nil {
        loggerSession.Error("encoding-response-failed", err)
        w.WriteHeader(http.StatusInternalServerError)

        return
    }
}

func (a *App) Update(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
    loggerSession := a.logger.Session("update-app", lager.Data{"guid": ps.ByName("process_guid")})

    var request cf.UpdateDesiredLRPRequest

    if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
        loggerSession.Error("json-decoding-failed", err)

        writeUpdateErrorResponse(w, err, http.StatusBadRequest, loggerSession)

        return
    }

    loggerSession.Debug("requested", lager.Data{"version": request.Version})

    if err := a.lrpBifrost.Update(r.Context(), request); err != nil {
        loggerSession.Error("bifrost-failed", err)
        writeUpdateErrorResponse(w, err, http.StatusInternalServerError, loggerSession)
    }
}

func (a *App) Stop(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
    loggerSession := a.logger.Session("stop-app", lager.Data{"guid": ps.ByName("process_guid"), "version": ps.ByName("version")})
    loggerSession.Debug("requested")

    identifier := api.LRPIdentifier{
        GUID:    ps.ByName("process_guid"),
        Version: ps.ByName("version_guid"),
    }

    if err := a.lrpBifrost.Stop(r.Context(), identifier); err != nil {
        loggerSession.Error("bifrost-failed", err)

        statusCode := http.StatusInternalServerError
        w.WriteHeader(statusCode)
    }
}

func (a *App) StopInstance(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
    loggerSession := a.logger.Session("stop-app-instance", lager.Data{"guid": ps.ByName("process_guid"), "version": ps.ByName("version_guid")})
    loggerSession.Debug("requested")

    identifier := api.LRPIdentifier{
        GUID:    ps.ByName("process_guid"),
        Version: ps.ByName("version_guid"),
    }

    index, err := strconv.ParseUint(ps.ByName("instance"), 10, 32) // nolint:gomnd
    if err != nil {
        loggerSession.Error("parsing-instance-index-failed", err)
        w.WriteHeader(http.StatusBadRequest)
    }

    if err := a.lrpBifrost.StopInstance(r.Context(), identifier, uint(index)); err != nil {
        loggerSession.Error("bifrost-failed", err)

        statusCode := http.StatusInternalServerError

        if errors.Is(err, eirini.ErrInvalidInstanceIndex) {
            statusCode = http.StatusBadRequest
        }

        w.WriteHeader(statusCode)
    }
}

func writeUpdateErrorResponse(w http.ResponseWriter, err error, statusCode int, loggerSession lager.Logger) {
    w.WriteHeader(statusCode)

    response := cf.DesiredLRPLifecycleResponse{
        Error: cf.Error{
            Message: err.Error(),
        },
    }

    body, marshalError := json.Marshal(response)
    if marshalError != nil {
        loggerSession.Error("could-not-marshal-response", marshalError)

        return
    }

    if _, err = w.Write(body); err != nil {
        loggerSession.Error("could-not-write-response", err)
    }
}