Skip to content

Commit

Permalink
Add POST /v3/service_instances endpoint
Browse files Browse the repository at this point in the history
Issue #430

Co-authored-by: Dave Walter <[email protected]>
  • Loading branch information
akrishna90 and davewalter committed Jan 26, 2022
1 parent 66ed165 commit 29e9b1f
Show file tree
Hide file tree
Showing 19 changed files with 1,136 additions and 33 deletions.
123 changes: 123 additions & 0 deletions api/apis/fake/cfservice_instance_repository.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 9 additions & 9 deletions api/apis/integration/route_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,10 @@ var _ = Describe("Route Handler", func() {

BeforeEach(func() {
createRoleBinding(ctx, userName, spaceDeveloperRole.Name, namespace.Name)

routeGUID = generateGUID()
domainGUID = generateGUID()

cfDomain := &networkingv1alpha1.CFDomain{
ObjectMeta: metav1.ObjectMeta{
Name: domainGUID,
Expand All @@ -82,7 +82,7 @@ var _ = Describe("Route Handler", func() {
Expect(
k8sClient.Create(ctx, cfDomain),
).To(Succeed())

cfRoute = &networkingv1alpha1.CFRoute{
ObjectMeta: metav1.ObjectMeta{
Name: routeGUID,
Expand Down Expand Up @@ -111,27 +111,27 @@ var _ = Describe("Route Handler", func() {
Expect(
k8sClient.Create(ctx, cfRoute),
).To(Succeed())

Eventually(func() error {
route := &networkingv1alpha1.CFRoute{}
return k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace.Name, Name: routeGUID}, route)
}).ShouldNot(HaveOccurred())

var err error
req, err = http.NewRequestWithContext(ctx, "DELETE", serverURI("/v3/routes/"+routeGUID), strings.NewReader(""))
Expect(err).NotTo(HaveOccurred())

req.Header.Add("Content-type", "application/json")
})

JustBeforeEach(func() {
router.ServeHTTP(rr, req)
})

It("returns 202 and eventually deletes the route", func() {
testCtx := context.Background()
Expect(rr.Code).To(Equal(202))

Eventually(func() error {
route := &networkingv1alpha1.CFRoute{}
return k8sClient.Get(testCtx, client.ObjectKey{Namespace: namespace.Name, Name: routeGUID}, route)
Expand Down
116 changes: 116 additions & 0 deletions api/apis/service_instance_handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package apis

import (
"context"
"net/http"
"net/url"

"code.cloudfoundry.org/cf-k8s-controllers/api/payloads"

"code.cloudfoundry.org/cf-k8s-controllers/api/presenter"

"code.cloudfoundry.org/cf-k8s-controllers/api/repositories"

"code.cloudfoundry.org/cf-k8s-controllers/api/authorization"

"github.com/gorilla/mux"

"github.com/go-logr/logr"
)

const (
ServiceInstanceCreateEndpoint = "/v3/service_instances"
)

//counterfeiter:generate -o fake -fake-name CFServiceInstanceRepository . CFServiceInstanceRepository
type CFServiceInstanceRepository interface {
CreateServiceInstance(context.Context, authorization.Info, repositories.CreateServiceInstanceMessage) (repositories.ServiceInstanceRecord, error)
}

type ServiceInstanceHandler struct {
logger logr.Logger
serverURL url.URL
serviceInstanceRepo CFServiceInstanceRepository
appRepo CFAppRepository
}

func NewServiceInstanceHandler(
logger logr.Logger,
serverURL url.URL,
serviceInstanceRepo CFServiceInstanceRepository,
appRepo CFAppRepository,
) *ServiceInstanceHandler {
return &ServiceInstanceHandler{
logger: logger,
serverURL: serverURL,
serviceInstanceRepo: serviceInstanceRepo,
appRepo: appRepo,
}
}

func (h *ServiceInstanceHandler) serviceInstanceCreateHandler(authInfo authorization.Info, w http.ResponseWriter, r *http.Request) {
ctx := context.Background()
w.Header().Set("Content-Type", "application/json")

var payload payloads.ServiceInstanceCreate
rme := decodeAndValidateJSONPayload(r, &payload)
if rme != nil {
writeRequestMalformedErrorResponse(w, rme)
return
}

namespaceGUID := payload.Relationships.Space.Data.GUID
_, err := h.appRepo.GetNamespace(ctx, authInfo, namespaceGUID)
if err != nil {
switch err.(type) {
case repositories.PermissionDeniedOrNotFoundError:
h.logger.Info("Namespace not found", "Namespace GUID", namespaceGUID)
writeUnprocessableEntityError(w, "Invalid space. Ensure that the space exists and you have access to it.")
return
default:
h.logger.Error(err, "Failed to fetch namespace from Kubernetes", "Namespace GUID", namespaceGUID)
writeUnknownErrorResponse(w)
return
}
}

serviceInstanceRecord, err := h.serviceInstanceRepo.CreateServiceInstance(ctx, authInfo, payload.ToServiceInstanceCreateMessage())
if err != nil {
if authorization.IsInvalidAuth(err) {
h.logger.Error(err, "unauthorized to create service instance")
writeInvalidAuthErrorResponse(w)

return
}

if authorization.IsNotAuthenticated(err) {
h.logger.Error(err, "unauthorized to create service instance")
writeNotAuthenticatedErrorResponse(w)

return
}

if repositories.IsForbiddenError(err) {
h.logger.Error(err, "not allowed to create service instance")
writeNotAuthorizedErrorResponse(w)

return
}

h.logger.Error(err, "Failed to create service instance", "Service Instance Name", serviceInstanceRecord.Name)
writeUnknownErrorResponse(w)
return
}

err = writeJsonResponse(w, presenter.ForServiceInstance(serviceInstanceRecord, h.serverURL), http.StatusCreated)
if err != nil {
// untested
h.logger.Error(err, "Failed to render response", "ServiceInstance Name", payload.Name)
writeUnknownErrorResponse(w)
}
}

func (h *ServiceInstanceHandler) RegisterRoutes(router *mux.Router) {
w := NewAuthAwareHandlerFuncWrapper(h.logger)
router.Path(ServiceInstanceCreateEndpoint).Methods("POST").HandlerFunc(w.Wrap(h.serviceInstanceCreateHandler))
}
Loading

0 comments on commit 29e9b1f

Please sign in to comment.