handler/app_handler.go
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)
}
}