Skip to content

Commit

Permalink
Merge branch 'async-bind-ready-for-pr' of https://github.com/liorokma…
Browse files Browse the repository at this point in the history
…n/brokerapi into pr-55

Signed-off-by: Jack Newberry <[email protected]>
Signed-off-by: Kieron Browne <[email protected]>
  • Loading branch information
Kieron Browne committed Sep 20, 2018
2 parents 7c841e3 + 49ecaf5 commit 05e8bae
Show file tree
Hide file tree
Showing 8 changed files with 445 additions and 62 deletions.
225 changes: 197 additions & 28 deletions api.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,25 @@ package brokerapi
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"strconv"
"strings"

"code.cloudfoundry.org/lager"
"github.com/gorilla/mux"
"github.com/pivotal-cf/brokerapi/auth"
)

const (
provisionLogKey = "provision"
deprovisionLogKey = "deprovision"
bindLogKey = "bind"
unbindLogKey = "unbind"
updateLogKey = "update"
lastOperationLogKey = "lastOperation"
catalogLogKey = "catalog"
provisionLogKey = "provision"
deprovisionLogKey = "deprovision"
bindLogKey = "bind"
getBindLogKey = "getBinding"
unbindLogKey = "unbind"
updateLogKey = "update"
lastOperationLogKey = "lastOperation"
lastBindingOperationLogKey = "lastBindingOperation"
catalogLogKey = "catalog"

instanceIDLogKey = "instance-id"
instanceDetailsLogKey = "instance-details"
Expand All @@ -47,6 +49,7 @@ const (
bindingAlreadyExistsErrorKey = "binding-already-exists"
instanceMissingErrorKey = "instance-missing"
bindingMissingErrorKey = "binding-missing"
bindingNotFoundErrorKey = "binding-not-found"
asyncRequiredKey = "async-required"
planChangeNotSupportedKey = "plan-change-not-supported"
unknownErrorKey = "unknown-error"
Expand Down Expand Up @@ -86,8 +89,11 @@ func AttachRoutes(router *mux.Router, serviceBroker ServiceBroker, logger lager.
router.HandleFunc("/v2/service_instances/{instance_id}/last_operation", handler.lastOperation).Methods("GET")
router.HandleFunc("/v2/service_instances/{instance_id}", handler.update).Methods("PATCH")

router.HandleFunc("/v2/service_instances/{instance_id}/service_bindings/{binding_id}", handler.getBinding).Methods("GET")
router.HandleFunc("/v2/service_instances/{instance_id}/service_bindings/{binding_id}", handler.bind).Methods("PUT")
router.HandleFunc("/v2/service_instances/{instance_id}/service_bindings/{binding_id}", handler.unbind).Methods("DELETE")

router.HandleFunc("/v2/service_instances/{instance_id}/service_bindings/{binding_id}/last_operation", handler.lastBindingOperation).Methods("GET")
}

type serviceBrokerHandler struct {
Expand All @@ -98,7 +104,8 @@ type serviceBrokerHandler struct {
func (h serviceBrokerHandler) catalog(w http.ResponseWriter, req *http.Request) {
logger := h.logger.Session(catalogLogKey, lager.Data{})

if err := checkBrokerAPIVersionHdr(req); err != nil {
if _, err := checkBrokerAPIVersionHdr(req); err != nil {
logger.Error("Check failed", err)
h.respond(w, http.StatusPreconditionFailed, ErrorResponse{
Description: err.Error(),
})
Expand Down Expand Up @@ -129,7 +136,7 @@ func (h serviceBrokerHandler) provision(w http.ResponseWriter, req *http.Request
instanceIDLogKey: instanceID,
})

if err := checkBrokerAPIVersionHdr(req); err != nil {
if _, err := checkBrokerAPIVersionHdr(req); err != nil {
h.respond(w, http.StatusPreconditionFailed, ErrorResponse{
Description: err.Error(),
})
Expand Down Expand Up @@ -237,7 +244,7 @@ func (h serviceBrokerHandler) update(w http.ResponseWriter, req *http.Request) {
instanceIDLogKey: instanceID,
})

if err := checkBrokerAPIVersionHdr(req); err != nil {
if _, err := checkBrokerAPIVersionHdr(req); err != nil {
h.respond(w, http.StatusPreconditionFailed, ErrorResponse{
Description: err.Error(),
})
Expand Down Expand Up @@ -293,7 +300,7 @@ func (h serviceBrokerHandler) deprovision(w http.ResponseWriter, req *http.Reque
instanceIDLogKey: instanceID,
})

if err := checkBrokerAPIVersionHdr(req); err != nil {
if _, err := checkBrokerAPIVersionHdr(req); err != nil {
h.respond(w, http.StatusPreconditionFailed, ErrorResponse{
Description: err.Error(),
})
Expand Down Expand Up @@ -346,6 +353,58 @@ func (h serviceBrokerHandler) deprovision(w http.ResponseWriter, req *http.Reque
}
}

func (h serviceBrokerHandler) getBinding(w http.ResponseWriter, req *http.Request) {
vars := mux.Vars(req)
instanceID := vars["instance_id"]
bindingID := vars["binding_id"]

logger := h.logger.Session(getBindLogKey, lager.Data{
instanceIDLogKey: instanceID,
bindingIDLogKey: bindingID,
})

versionCompatibility, err := checkBrokerAPIVersionHdr(req)
if err != nil {
h.respond(w, http.StatusPreconditionFailed, ErrorResponse{
Description: err.Error(),
})
logger.Error(apiVersionInvalidKey, err)
return
}
if versionCompatibility.Minor < 14 {
h.respond(w, http.StatusPreconditionFailed, ErrorResponse{
Description: "get binding endpoint only supported starting with OSB version 2.14",
})
logger.Error(apiVersionInvalidKey, err)
return
}

binding, err := h.serviceBroker.GetBinding(req.Context(), instanceID, bindingID)
if err != nil {
switch err := err.(type) {
case *FailureResponse:
logger.Error(err.LoggerAction(), err)
h.respond(w, err.ValidatedStatusCode(logger), err.ErrorResponse())
default:
logger.Error(unknownErrorKey, err)
h.respond(w, http.StatusInternalServerError, ErrorResponse{
Description: err.Error(),
})
}
return
}

h.respond(w, http.StatusOK, GetBindingResponse{
BindingResponse: BindingResponse{
Credentials: binding.Credentials,
SyslogDrainURL: binding.SyslogDrainURL,
RouteServiceURL: binding.RouteServiceURL,
VolumeMounts: binding.VolumeMounts,
},
Parameters: binding.Parameters,
})
}

func (h serviceBrokerHandler) bind(w http.ResponseWriter, req *http.Request) {
vars := mux.Vars(req)
instanceID := vars["instance_id"]
Expand All @@ -356,7 +415,8 @@ func (h serviceBrokerHandler) bind(w http.ResponseWriter, req *http.Request) {
bindingIDLogKey: bindingID,
})

if err := checkBrokerAPIVersionHdr(req); err != nil {
versionCompatibility, err := checkBrokerAPIVersionHdr(req)
if err != nil {
h.respond(w, http.StatusPreconditionFailed, ErrorResponse{
Description: err.Error(),
})
Expand Down Expand Up @@ -389,7 +449,16 @@ func (h serviceBrokerHandler) bind(w http.ResponseWriter, req *http.Request) {
return
}

binding, err := h.serviceBroker.Bind(req.Context(), instanceID, bindingID, details)
asyncAllowed := req.FormValue("accepts_incomplete") == "true"
if asyncAllowed && versionCompatibility.Minor < 14 {
h.respond(w, http.StatusUnprocessableEntity, ErrorResponse{
Description: "async binding only supported from OSB version 2.14 and up",
})
logger.Error(apiVersionInvalidKey, err)
return
}

binding, err := h.serviceBroker.Bind(req.Context(), instanceID, bindingID, details, asyncAllowed)
if err != nil {
switch err := err.(type) {
case *FailureResponse:
Expand All @@ -413,8 +482,14 @@ func (h serviceBrokerHandler) bind(w http.ResponseWriter, req *http.Request) {
return
}

brokerAPIVersion := req.Header.Get("X-Broker-Api-Version")
if brokerAPIVersion == "2.8" || brokerAPIVersion == "2.9" {
if binding.IsAsync {
h.respond(w, http.StatusAccepted, AsyncBindResponse{
OperationData: binding.OperationData,
})
return
}

if versionCompatibility.Minor == 8 || versionCompatibility.Minor == 9 {
experimentalVols := []ExperimentalVolumeMount{}

for _, vol := range binding.VolumeMounts {
Expand Down Expand Up @@ -446,7 +521,12 @@ func (h serviceBrokerHandler) bind(w http.ResponseWriter, req *http.Request) {
return
}

h.respond(w, http.StatusCreated, binding)
h.respond(w, http.StatusCreated, BindingResponse{
Credentials: binding.Credentials,
SyslogDrainURL: binding.SyslogDrainURL,
RouteServiceURL: binding.RouteServiceURL,
VolumeMounts: binding.VolumeMounts,
})
}

func (h serviceBrokerHandler) unbind(w http.ResponseWriter, req *http.Request) {
Expand All @@ -459,7 +539,8 @@ func (h serviceBrokerHandler) unbind(w http.ResponseWriter, req *http.Request) {
bindingIDLogKey: bindingID,
})

if err := checkBrokerAPIVersionHdr(req); err != nil {
versionCompatibility, err := checkBrokerAPIVersionHdr(req)
if err != nil {
h.respond(w, http.StatusPreconditionFailed, ErrorResponse{
Description: err.Error(),
})
Expand Down Expand Up @@ -488,7 +569,17 @@ func (h serviceBrokerHandler) unbind(w http.ResponseWriter, req *http.Request) {
return
}

if err := h.serviceBroker.Unbind(req.Context(), instanceID, bindingID, details); err != nil {
asyncAllowed := req.FormValue("accepts_incomplete") == "true"
if asyncAllowed && versionCompatibility.Minor < 14 {
h.respond(w, http.StatusUnprocessableEntity, ErrorResponse{
Description: "async unbinding only supported from OSB version 2.14 and up",
})
logger.Error(apiVersionInvalidKey, err)
return
}

unbindResponse, err := h.serviceBroker.Unbind(req.Context(), instanceID, bindingID, details, asyncAllowed)
if err != nil {
switch err := err.(type) {
case *FailureResponse:
logger.Error(err.LoggerAction(), err)
Expand All @@ -502,19 +593,88 @@ func (h serviceBrokerHandler) unbind(w http.ResponseWriter, req *http.Request) {
return
}

h.respond(w, http.StatusOK, EmptyResponse{})
if unbindResponse.IsAsync {
h.respond(w, http.StatusAccepted, UnbindResponse{
OperationData: unbindResponse.OperationData,
})
} else {
h.respond(w, http.StatusOK, EmptyResponse{})
}

}

func (h serviceBrokerHandler) lastBindingOperation(w http.ResponseWriter, req *http.Request) {
vars := mux.Vars(req)
instanceID := vars["instance_id"]
bindingID := vars["binding_id"]
pollDetails := PollDetails{
PlanID: req.FormValue("plan_id"),
ServiceID: req.FormValue("service_id"),
OperationData: req.FormValue("operation"),
}

logger := h.logger.Session(lastBindingOperationLogKey, lager.Data{
instanceIDLogKey: instanceID,
})

versionCompatibility, err := checkBrokerAPIVersionHdr(req)
if err != nil {
h.respond(w, http.StatusPreconditionFailed, ErrorResponse{
Description: err.Error(),
})
logger.Error(apiVersionInvalidKey, err)
return
}
if versionCompatibility.Minor < 14 {
h.respond(w, http.StatusPreconditionFailed, ErrorResponse{
Description: "get binding endpoint only supported starting with OSB version 2.14",
})
logger.Error(apiVersionInvalidKey, err)
return
}

logger.Info("starting-check-for-binding-operation")

lastOperation, err := h.serviceBroker.LastBindingOperation(req.Context(), instanceID, bindingID, pollDetails)

if err != nil {
switch err := err.(type) {
case *FailureResponse:
logger.Error(err.LoggerAction(), err)
h.respond(w, err.ValidatedStatusCode(logger), err.ErrorResponse())
default:
logger.Error(unknownErrorKey, err)
h.respond(w, http.StatusInternalServerError, ErrorResponse{
Description: err.Error(),
})
}
return
}

logger.WithData(lager.Data{"state": lastOperation.State}).Info("done-check-for-binding-operation")

lastOperationResponse := LastOperationResponse{
State: lastOperation.State,
Description: lastOperation.Description,
}
h.respond(w, http.StatusOK, lastOperationResponse)

}

func (h serviceBrokerHandler) lastOperation(w http.ResponseWriter, req *http.Request) {
vars := mux.Vars(req)
instanceID := vars["instance_id"]
operationData := req.FormValue("operation")
pollDetails := PollDetails{
PlanID: req.FormValue("plan_id"),
ServiceID: req.FormValue("service_id"),
OperationData: req.FormValue("operation"),
}

logger := h.logger.Session(lastOperationLogKey, lager.Data{
instanceIDLogKey: instanceID,
})

if err := checkBrokerAPIVersionHdr(req); err != nil {
if _, err := checkBrokerAPIVersionHdr(req); err != nil {
h.respond(w, http.StatusPreconditionFailed, ErrorResponse{
Description: err.Error(),
})
Expand All @@ -524,7 +684,7 @@ func (h serviceBrokerHandler) lastOperation(w http.ResponseWriter, req *http.Req

logger.Info("starting-check-for-operation")

lastOperation, err := h.serviceBroker.LastOperation(req.Context(), instanceID, operationData)
lastOperation, err := h.serviceBroker.LastOperation(req.Context(), instanceID, pollDetails)

if err != nil {
switch err := err.(type) {
Expand Down Expand Up @@ -561,14 +721,23 @@ func (h serviceBrokerHandler) respond(w http.ResponseWriter, status int, respons
}
}

func checkBrokerAPIVersionHdr(req *http.Request) error {
type brokerVersion struct {
Major int
Minor int
}

func checkBrokerAPIVersionHdr(req *http.Request) (brokerVersion, error) {
var version brokerVersion
apiVersion := req.Header.Get("X-Broker-API-Version")
if apiVersion == "" {
return errors.New("X-Broker-API-Version Header not set")
return version, errors.New("X-Broker-API-Version Header not set")
}
if n, err := fmt.Sscanf(apiVersion, "%d.%d", &version.Major, &version.Minor); err != nil || n < 2 {
return version, errors.New("X-Broker-API-Version Header must contain a version")
}

if !strings.HasPrefix(apiVersion, "2.") {
return errors.New("X-Broker-API-Version Header must be 2.x")
if version.Major != 2 {
return version, errors.New("X-Broker-API-Version Header must be 2.x")
}
return nil
return version, nil
}
Loading

0 comments on commit 05e8bae

Please sign in to comment.