Skip to content

Commit

Permalink
Instance binding parameters 🚧 WIP 🚧 (#558)
Browse files Browse the repository at this point in the history
* parameters

* adding more tests

* test description change

* tests fix

* typo

* fixes after code review

* revert

* reverting capital letters back, because tests are failing

* trigger tests

* update unit test

* fix test

* after reformating

* fix for test

* after code review

* go convention to use lower case in errors

* remove FDescribe:

* after refview

* after reformating
  • Loading branch information
sigalmaya authored Oct 6, 2020
1 parent 5536946 commit fb7b619
Show file tree
Hide file tree
Showing 10 changed files with 433 additions and 50 deletions.
50 changes: 1 addition & 49 deletions api/osb/catalog_fetcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,6 @@ package osb
import (
"context"
"fmt"
"github.com/Peripli/service-manager/pkg/client"
"net"
"net/http"

"github.com/Peripli/service-manager/pkg/log"
"github.com/Peripli/service-manager/pkg/types"
"github.com/Peripli/service-manager/pkg/util"
)
Expand All @@ -34,49 +29,6 @@ const brokerAPIVersionHeader = "X-Broker-API-Version"
// CatalogFetcher creates a broker catalog fetcher that uses the provided request function to call the specified broker's catalog endpoint
func CatalogFetcher(doRequestWithClient util.DoRequestWithClientFunc, brokerAPIVersion string) func(ctx context.Context, broker *types.ServiceBroker) ([]byte, error) {
return func(ctx context.Context, broker *types.ServiceBroker) ([]byte, error) {
log.C(ctx).Debugf("Attempting to fetch catalog from broker with name %s and URL %s", broker.Name, broker.BrokerURL)
brokerClient, err := client.NewBrokerClient(broker, doRequestWithClient)
if err != nil {
return nil, err
}

response, err := brokerClient.SendRequest(ctx, http.MethodGet, fmt.Sprintf(brokerCatalogURL, broker.BrokerURL),
map[string]string{}, nil, map[string]string{
brokerAPIVersionHeader: brokerAPIVersion,
})
if err != nil {
log.C(ctx).WithError(err).Errorf("Error while forwarding request to service broker %s", broker.Name)
return nil, &util.HTTPError{
ErrorType: "ServiceBrokerErr",
Description: fmt.Sprintf("could not reach service broker %s at %s", broker.Name, broker.BrokerURL),
StatusCode: http.StatusBadGateway,
}
}

if response.StatusCode != http.StatusOK {
log.C(ctx).WithError(err).Errorf("error fetching catalog for broker with name %s: %s", broker.Name, util.HandleResponseError(response))
return nil, &util.HTTPError{
ErrorType: "ServiceBrokerErr",
Description: fmt.Sprintf("error fetching catalog for broker with name %s: broker responded with %s", broker.Name, response.Status),
StatusCode: http.StatusBadRequest,
}
}

var responseBytes []byte
if responseBytes, err = util.BodyToBytes(response.Body); err != nil {
if nErr, ok := err.(net.Error); ok && nErr.Timeout() {
log.C(ctx).WithError(err).Errorf("error fetching catalog for broker with name %s: %s", broker.Name, err)
return nil, &util.HTTPError{
ErrorType: "ServiceBrokerErr",
Description: fmt.Sprintf("error fetching catalog for broker with name %s: timed out", broker.Name),
StatusCode: http.StatusGatewayTimeout,
}
}
return nil, fmt.Errorf("error getting content from body of response with status %s: %s", response.Status, err)
}

log.C(ctx).Debugf("Successfully fetched catalog from broker with name %s and URL %s", broker.Name, broker.BrokerURL)

return responseBytes, nil
return Get(doRequestWithClient, brokerAPIVersion, ctx, broker, fmt.Sprintf(brokerCatalogURL, broker.BrokerURL), "catalog")
}
}
2 changes: 1 addition & 1 deletion api/osb/catalog_fetcher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ var _ = Describe("Catalog CatalogFetcher", func() {
Body: simpleCatalog,
Err: nil,
},
expectedErr: fmt.Errorf("error fetching catalog for broker with name %s", name),
expectedErr: fmt.Errorf("error fetching catalog from URL %s/v2/catalog and broker with name %s", url, name),
expectedResponse: nil,
}),
Entry("returns error if sending request fails with error", testCase{
Expand Down
60 changes: 60 additions & 0 deletions api/osb/utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package osb

import (
"context"
"fmt"
"github.com/Peripli/service-manager/pkg/client"
"github.com/Peripli/service-manager/pkg/log"
"github.com/Peripli/service-manager/pkg/types"
"github.com/Peripli/service-manager/pkg/util"
"net"
"net/http"
)

func Get(doRequestWithClient util.DoRequestWithClientFunc, brokerAPIVersion string, ctx context.Context, broker *types.ServiceBroker, url string, resourceType string) ([]byte, error) {

log.C(ctx).Debugf("attempting to fetch %s from URL %s and broker with name %s", resourceType, url, broker.Name)
brokerClient, err := client.NewBrokerClient(broker, doRequestWithClient)
if err != nil {
return nil, err
}
response, err := brokerClient.SendRequest(ctx, http.MethodGet, url,
map[string]string{}, nil, map[string]string{
brokerAPIVersionHeader: brokerAPIVersion,
})
if err != nil {
log.C(ctx).WithError(err).Errorf("error while forwarding request to service broker %s", broker.Name)
return nil, &util.HTTPError{
ErrorType: "ServiceBrokerErr",
Description: fmt.Sprintf("could not reach service broker %s at %s", broker.Name, broker.BrokerURL),
StatusCode: http.StatusBadGateway,
}
}

if response.StatusCode != http.StatusOK {
log.C(ctx).WithError(err).Errorf("error fetching %s from URL %s and broker with name %s: %s", resourceType, url, broker.Name, util.HandleResponseError(response))
return nil, &util.HTTPError{
ErrorType: "ServiceBrokerErr",
Description: fmt.Sprintf("error fetching %s from URL %s and broker with name %s: %s", resourceType, url, broker.Name, response.Status),
StatusCode: http.StatusBadRequest,
}
}

var responseBytes []byte
if responseBytes, err = util.BodyToBytes(response.Body); err != nil {
if nErr, ok := err.(net.Error); ok && nErr.Timeout() {
log.C(ctx).WithError(err).Errorf("error fetching %s from URL %s and broker with name %s: %s: time out", resourceType, url, broker.Name, err)
return nil, &util.HTTPError{
ErrorType: "ServiceBrokerErr",
Description: fmt.Sprintf("error fetching %s from URL %s and broker with name %s: timed out", resourceType, url, broker.Name),
StatusCode: http.StatusGatewayTimeout,
}
}
return nil, fmt.Errorf("error getting content from body of response from %s with status %s: %s", url, response.Status, err)
}

log.C(ctx).Debugf("successfully fetched %s from URL %s and broker with name %s", resourceType, url, broker.Name)

return responseBytes, nil

}
69 changes: 69 additions & 0 deletions api/service_binding_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,30 @@ package api
import (
"context"
"fmt"
"github.com/Peripli/service-manager/api/osb"
"github.com/Peripli/service-manager/pkg/query"
"github.com/Peripli/service-manager/pkg/util"
"github.com/Peripli/service-manager/storage"
"net/http"

"github.com/Peripli/service-manager/pkg/types"
"github.com/Peripli/service-manager/pkg/web"
)

const serviceBindingOSBURL = "%s/v2/service_instances/%s/service_bindings/%s"

// ServiceBindingController implements api.Controller by providing service bindings API logic
type ServiceBindingController struct {
*BaseController
osbVersion string
}

func NewServiceBindingController(ctx context.Context, options *Options) *ServiceBindingController {
return &ServiceBindingController{
BaseController: NewAsyncController(ctx, options, web.ServiceBindingsURL, types.ServiceBindingType, true, func() types.Object {
return &types.ServiceBinding{}
}),
osbVersion: options.APISettings.OSBVersion,
}
}

Expand Down Expand Up @@ -68,6 +76,13 @@ func (c *ServiceBindingController) Routes() []web.Route {
},
Handler: c.ListObjects,
},
{
Endpoint: web.Endpoint{
Method: http.MethodGet,
Path: fmt.Sprintf("%s/{%s}/{%s}", c.resourceBaseURL, web.PathParamResourceID, web.ParametersURL),
},
Handler: c.GetParameters,
},
{
Endpoint: web.Endpoint{
Method: http.MethodDelete,
Expand All @@ -77,3 +92,57 @@ func (c *ServiceBindingController) Routes() []web.Route {
},
}
}
func (c *ServiceBindingController) GetParameters(r *web.Request) (*web.Response, error) {
isAsync := r.URL.Query().Get(web.QueryParamAsync)
if isAsync == "true" {
return nil, &util.HTTPError{
ErrorType: "InvalidRequest",
Description: fmt.Sprintf("requested %s api doesn't support asynchronous operation.", r.URL.RequestURI()),
StatusCode: http.StatusBadRequest,
}
}
ctx := r.Context()
serviceBindingId := r.PathParams[web.PathParamResourceID]
byID := query.ByField(query.EqualsOperator, "id", serviceBindingId)
serviceBindingObject, err := c.repository.Get(ctx, types.ServiceBindingType, byID)
if err != nil {
return nil, util.HandleStorageError(err, types.ServiceBindingType.String())
}
serviceBinding := serviceBindingObject.(*types.ServiceBinding)
service, err := storage.GetServiceOfferingByServiceInstanceId(c.repository, ctx, serviceBinding.ServiceInstanceID)
if err != nil {
return nil, err
}
brokerObject, err := c.repository.Get(ctx, types.ServiceBrokerType, query.ByField(query.EqualsOperator, "id", service.BrokerID))
if err != nil {
return nil, util.HandleStorageError(err, types.ServiceBrokerType.String())
}
broker := brokerObject.(*types.ServiceBroker)
if !service.BindingsRetrievable {
return nil, &util.HTTPError{
ErrorType: "BadRequest",
Description: fmt.Sprintf("this operation is not supported"),
StatusCode: http.StatusBadRequest,
}
}

serviceBindingBytes, err := osb.Get(util.ClientRequest, c.osbVersion, ctx,
broker,
fmt.Sprintf(serviceBindingOSBURL, broker.BrokerURL, serviceBinding.ServiceInstanceID, serviceBindingId),
types.ServiceBindingType.String())
if err != nil {
return nil, err
}

serviceBindingResponse := &types.ServiceBinding{}
if err := util.BytesToObject(serviceBindingBytes, &serviceBindingResponse); err != nil {
return nil, &util.HTTPError{
ErrorType: "ServiceBrokerErr",
Description: fmt.Sprintf("error reading parameters of service binding with id %s from broker %s", serviceBindingId, broker.BrokerURL),
StatusCode: http.StatusBadGateway,
}
}

return util.NewJSONResponse(http.StatusOK, &serviceBindingResponse.Parameters)

}
72 changes: 72 additions & 0 deletions api/service_instance_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,31 @@ package api
import (
"context"
"fmt"
"github.com/Peripli/service-manager/api/osb"
"github.com/Peripli/service-manager/pkg/log"
"github.com/Peripli/service-manager/pkg/query"
"github.com/Peripli/service-manager/pkg/types"
"github.com/Peripli/service-manager/pkg/util"
"github.com/Peripli/service-manager/pkg/web"
"github.com/Peripli/service-manager/storage"
"net/http"
)

const serviceInstanceOSBURL string = "%s/v2/service_instances/%s"

// ServiceInstanceController implements api.Controller by providing service Instances API logic
type ServiceInstanceController struct {
*BaseController
osbVersion string
}

func NewServiceInstanceController(ctx context.Context, options *Options) *ServiceInstanceController {

return &ServiceInstanceController{
BaseController: NewAsyncController(ctx, options, web.ServiceInstancesURL, types.ServiceInstanceType, true, func() types.Object {
return &types.ServiceInstance{}
}),
osbVersion: options.APISettings.OSBVersion,
}
}

Expand All @@ -53,6 +63,15 @@ func (c *ServiceInstanceController) Routes() []web.Route {
},
Handler: c.GetSingleObject,
},

{
Endpoint: web.Endpoint{
Method: http.MethodGet,
Path: fmt.Sprintf("%s/{%s}%s", c.resourceBaseURL, web.PathParamResourceID, web.ParametersURL),
},
Handler: c.GetParameters,
},

{
Endpoint: web.Endpoint{
Method: http.MethodGet,
Expand Down Expand Up @@ -83,3 +102,56 @@ func (c *ServiceInstanceController) Routes() []web.Route {
},
}
}

func (c *ServiceInstanceController) GetParameters(r *web.Request) (*web.Response, error) {
isAsync := r.URL.Query().Get(web.QueryParamAsync)
if isAsync == "true" {
return nil, &util.HTTPError{
ErrorType: "InvalidRequest",
Description: fmt.Sprintf("requested %s api doesn't support asynchronous operation.", r.URL.RequestURI()),
StatusCode: http.StatusBadRequest,
}
}
serviceInstanceId := r.PathParams[web.PathParamResourceID]
ctx := r.Context()
log.C(ctx).Debugf("getting %s with id %s", c.objectType, serviceInstanceId)

service, err := storage.GetServiceOfferingByServiceInstanceId(c.repository, ctx, serviceInstanceId)
if err != nil {
return nil, err
}

brokerObject, err := c.repository.Get(ctx, types.ServiceBrokerType, query.ByField(query.EqualsOperator, "id", service.BrokerID))
if err != nil {
return nil, util.HandleStorageError(err, types.ServiceBrokerType.String())
}
broker := brokerObject.(*types.ServiceBroker)
if !service.InstancesRetrievable {
return nil, &util.HTTPError{
ErrorType: "BadRequest",
Description: fmt.Sprintf("this operation is not supported."),
StatusCode: http.StatusBadRequest,
}

}

serviceInstanceBytes, err := osb.Get(util.ClientRequest, c.osbVersion, ctx,
broker,
fmt.Sprintf(serviceInstanceOSBURL, broker.BrokerURL, serviceInstanceId),
types.ServiceInstanceType.String())

if err != nil {
return nil, err
}

serviceResponse := &types.ServiceInstance{}
if err := util.BytesToObject(serviceInstanceBytes, &serviceResponse); err != nil {
return nil, &util.HTTPError{
ErrorType: "ServiceBrokerErr",
Description: fmt.Sprintf("error reading parameters of service instance with id %s from broker %s", serviceInstanceId, broker.BrokerURL),
StatusCode: http.StatusBadGateway,
}
}

return util.NewJSONResponse(http.StatusOK, &serviceResponse.Parameters)
}
2 changes: 2 additions & 0 deletions pkg/web/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ const (
// ResourceOperationsURL is the URL path fetch operations for a resource
ResourceOperationsURL = "/operations"

ParametersURL = "/parameters"

// OperationsURL is the operations API base URL path
OperationsURL = "/" + apiVersion + "/operations"

Expand Down
29 changes: 29 additions & 0 deletions storage/utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package storage

import (
"context"
"github.com/Peripli/service-manager/pkg/query"
"github.com/Peripli/service-manager/pkg/types"
"github.com/Peripli/service-manager/pkg/util"
)

func GetServiceOfferingByServiceInstanceId(repository Repository, ctx context.Context, serviceInstanceId string) (*types.ServiceOffering, error) {
byID := query.ByField(query.EqualsOperator, "id", serviceInstanceId)
criteria := query.CriteriaForContext(ctx)
obj, err := repository.Get(ctx, types.ServiceInstanceType, append(criteria, byID)...)
if err != nil {
return nil, util.HandleStorageError(err, types.ServiceInstanceType.String())
}
serviceInstance := obj.(*types.ServiceInstance)
planObject, err := repository.Get(ctx, types.ServicePlanType, query.ByField(query.EqualsOperator, "id", serviceInstance.ServicePlanID))
if err != nil {
return nil, util.HandleStorageError(err, types.ServicePlanType.String())
}
plan := planObject.(*types.ServicePlan)
serviceObject, err := repository.Get(ctx, types.ServiceOfferingType, query.ByField(query.EqualsOperator, "id", plan.ServiceOfferingID))
if err != nil {
return nil, util.HandleStorageError(err, types.ServiceOfferingType.String())
}
service := serviceObject.(*types.ServiceOffering)
return service, nil
}
Loading

0 comments on commit fb7b619

Please sign in to comment.