From 8fe7688d54e506c061de3b5fea0ebdc1cd9dbad9 Mon Sep 17 00:00:00 2001 From: Dimitar Petrov Date: Thu, 19 Mar 2020 17:37:06 +0200 Subject: [PATCH] Write APIs CLI commands (#64) --- .gometalinter.json | 2 +- .travis.yml | 2 +- Gopkg.lock | 48 +- internal/cmd/binding/bind.go | 118 ++ internal/cmd/binding/bind_test.go | 223 +++ internal/cmd/binding/binding_suite_test.go | 12 + internal/cmd/binding/get_binding.go | 19 +- internal/cmd/binding/get_binding_test.go | 37 +- internal/cmd/binding/list_bindings.go | 9 + internal/cmd/binding/list_bindings_test.go | 21 +- internal/cmd/binding/unbind.go | 143 ++ internal/cmd/binding/unbind_test.go | 159 +++ internal/cmd/broker/broker_suite_test.go | 12 + internal/cmd/broker/delete_broker.go | 42 +- internal/cmd/broker/delete_broker_test.go | 80 +- internal/cmd/broker/get_broker.go | 6 +- internal/cmd/broker/get_broker_test.go | 7 - internal/cmd/broker/list_brokers_test.go | 7 - internal/cmd/broker/register_broker.go | 7 +- internal/cmd/broker/register_broker_test.go | 73 +- internal/cmd/broker/update_broker.go | 17 +- internal/cmd/broker/update_broker_test.go | 54 +- internal/cmd/commander.go | 32 + internal/cmd/instance/deprovision.go | 126 ++ internal/cmd/instance/deprovision_test.go | 155 ++ internal/cmd/instance/get_instance.go | 15 +- internal/cmd/instance/get_instance_test.go | 11 +- internal/cmd/instance/instance_suite_test.go | 12 + internal/cmd/instance/list_instances_test.go | 9 +- internal/cmd/instance/provision.go | 159 +++ internal/cmd/instance/provision_test.go | 261 ++++ internal/cmd/label/label.go | 6 +- internal/cmd/label/label_test.go | 4 +- internal/cmd/offering/list_offerings_test.go | 9 +- internal/cmd/offering/marketplace_test.go | 7 - internal/cmd/offering/offering_suite_test.go | 12 + internal/cmd/platform/delete_platform_test.go | 9 +- internal/cmd/platform/list_platforms_test.go | 7 - internal/cmd/platform/platform_suite_test.go | 12 + .../cmd/platform/register_platform_test.go | 7 - internal/cmd/platform/update_platform_test.go | 7 - internal/cmd/status/status.go | 90 ++ internal/cmd/status/status_test.go | 80 ++ .../cmd/visibility/delete_visibility_test.go | 11 +- .../cmd/visibility/list_visibilities_test.go | 7 - .../visibility/register_visibility_test.go | 7 - .../cmd/visibility/update_visibility_test.go | 7 - .../cmd/visibility/visibility_suite_test.go | 12 + main.go | 6 + pkg/smclient/client.go | 156 ++- pkg/smclient/client_test.go | 1243 ----------------- pkg/smclient/smclientfakes/fake_client.go | 589 ++++++-- pkg/smclient/test/binding_test.go | 317 +++++ pkg/smclient/test/broker_test.go | 384 +++++ pkg/smclient/test/client_test.go | 31 + pkg/smclient/test/info_test.go | 86 ++ pkg/smclient/test/instance_test.go | 318 +++++ pkg/smclient/test/label_test.go | 43 + pkg/smclient/test/marketplace_test.go | 80 ++ pkg/smclient/test/platform_test.go | 276 ++++ pkg/smclient/test/smclient_suite_test.go | 155 ++ pkg/smclient/test/status_test.go | 66 + pkg/smclient/test/visibility_test.go | 279 ++++ pkg/types/broker.go | 11 +- pkg/types/label_changes.go | 4 +- pkg/types/operation.go | 61 + pkg/types/platform.go | 6 +- pkg/types/service_binding.go | 38 +- pkg/types/service_instance.go | 21 +- pkg/types/service_offering.go | 6 +- pkg/types/service_plan.go | 6 +- pkg/types/tableData.go | 86 +- pkg/types/visibility.go | 15 +- 73 files changed, 4743 insertions(+), 1712 deletions(-) create mode 100644 internal/cmd/binding/bind.go create mode 100644 internal/cmd/binding/bind_test.go create mode 100644 internal/cmd/binding/binding_suite_test.go create mode 100644 internal/cmd/binding/unbind.go create mode 100644 internal/cmd/binding/unbind_test.go create mode 100644 internal/cmd/broker/broker_suite_test.go create mode 100644 internal/cmd/instance/deprovision.go create mode 100644 internal/cmd/instance/deprovision_test.go create mode 100644 internal/cmd/instance/instance_suite_test.go create mode 100644 internal/cmd/instance/provision.go create mode 100644 internal/cmd/instance/provision_test.go create mode 100644 internal/cmd/offering/offering_suite_test.go create mode 100644 internal/cmd/platform/platform_suite_test.go create mode 100644 internal/cmd/status/status.go create mode 100644 internal/cmd/status/status_test.go create mode 100644 internal/cmd/visibility/visibility_suite_test.go delete mode 100644 pkg/smclient/client_test.go create mode 100644 pkg/smclient/test/binding_test.go create mode 100644 pkg/smclient/test/broker_test.go create mode 100644 pkg/smclient/test/client_test.go create mode 100644 pkg/smclient/test/info_test.go create mode 100644 pkg/smclient/test/instance_test.go create mode 100644 pkg/smclient/test/label_test.go create mode 100644 pkg/smclient/test/marketplace_test.go create mode 100644 pkg/smclient/test/platform_test.go create mode 100644 pkg/smclient/test/smclient_suite_test.go create mode 100644 pkg/smclient/test/status_test.go create mode 100644 pkg/smclient/test/visibility_test.go create mode 100644 pkg/types/operation.go diff --git a/.gometalinter.json b/.gometalinter.json index 52973390..11fb5783 100644 --- a/.gometalinter.json +++ b/.gometalinter.json @@ -1,5 +1,5 @@ { - "Enable": ["deadcode", "golint", "vet", "gotype", "gocyclo", "maligned", "errcheck", "misspell", "staticcheck"], + "Enable": ["deadcode", "golint", "vet", "gotype", "gocyclo", "errcheck", "misspell", "staticcheck"], "Exclude": ["test", "fake_*"], "Format": "[{{.Severity}}]: {{.Path}}: {{.Line}}:{{if .Col}}{{.Col}}{{end}}: {{.Message}} ({{.Linter}})", "Deadline": "120s", diff --git a/.travis.yml b/.travis.yml index e91f783d..4f20f2fe 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,7 +20,7 @@ before_script: script: # Execute static checks - - gometalinter --vendor ./... + - gometalinter --cyclo-over=13 --vendor ./... # Execute tests and generate coverage for all the packages except fakes and tests - go test ./... -coverpkg $(go list ./... | egrep -v "fakes|test" | paste -sd "," -) -coverprofile=profile.cov diff --git a/Gopkg.lock b/Gopkg.lock index 3eafad2f..ef9e113b 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -21,7 +21,7 @@ version = "v1.0.1" [[projects]] - digest = "1:a09a39929ade710ba81cdb7e94670d0d00b9b16d73928fa1fe493b6fd7852c5c" + digest = "1:8acbbe8e0bb2411300963a0186650d42e2ca5a261e319b15ca7d187245b9841b" name = "github.com/Peripli/service-manager" packages = [ "pkg/health", @@ -34,8 +34,8 @@ "pkg/web", ] pruneopts = "UT" - revision = "fe1b4ba4cd5f95f18c277d3067f7accb682f4743" - version = "v0.10.0" + revision = "8adb0c510e5959124b4f1c7447b26fa6d6329205" + version = "v0.10.3" [[projects]] digest = "1:3076a0751f5648f3ab1838abfba18bc5bf514cde7e3957500b62fba90633479a" @@ -46,12 +46,12 @@ version = "4.8" [[projects]] - digest = "1:abeb38ade3f32a92943e5be54f55ed6d6e3b6602761d74b4aab4c9dd45c18abd" + digest = "1:80057945464ffb5b0da1f026beb8df0e8dbd098eaf771a349291bed2cd29a83e" name = "github.com/fsnotify/fsnotify" packages = ["."] pruneopts = "UT" - revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9" - version = "v1.4.7" + revision = "45d7d09e39ef4ac08d493309fa031790c15bfe8a" + version = "v1.4.9" [[projects]] digest = "1:9ae31ce33b4bab257668963e844d98765b44160be4ee98cafc44637a213e530d" @@ -83,8 +83,8 @@ name = "github.com/golang/protobuf" packages = ["proto"] pruneopts = "UT" - revision = "d23c5127dc24889085f8ccea5c9d560a57a879d8" - version = "v1.3.3" + revision = "5d5b4c10bd43f85e63bd9e4a3fa9b1ea2ef88af2" + version = "v1.3.4" [[projects]] digest = "1:c0d19ab64b32ce9fe5cf4ddceba78d5bc9807f0016db6b1183599da3dcc24d10" @@ -244,12 +244,12 @@ version = "v1.3.1" [[projects]] - digest = "1:e096613fb7cf34743d49af87d197663cfccd61876e2219853005a57baedfa562" + digest = "1:6a13467c76490975b05609344b3d882a9185fd11a44a04ede7427d684e0819b9" name = "github.com/spf13/cobra" packages = ["."] pruneopts = "UT" - revision = "f2b07da1e2c38d5f12845a4f607e2e1018cbb1f5" - version = "v0.0.5" + revision = "0da06874266c88228b8f14615396a1f6bfc90ed7" + version = "v0.0.6" [[projects]] digest = "1:1b753ec16506f5864d26a28b43703c58831255059644351bbcb019b843950900" @@ -284,12 +284,12 @@ version = "v1.2.0" [[projects]] - digest = "1:13503b1b68ee704913caf452f02968fa313a9934e67d951ed0d39ca8e230d5e0" + digest = "1:358f9a53d3bb46b9dd07b4490f9b30f9f85218b41f6bbb48d6503668a45721a6" name = "github.com/tidwall/gjson" packages = ["."] pruneopts = "UT" - revision = "0360deb6d803e8c271363ce5f6c85d6cd843a3a0" - version = "v1.5.0" + revision = "f042915ca17de35980544c91ab2c8ceb73b682f2" + version = "v1.6.0" [[projects]] digest = "1:8453ddbed197809ee8ca28b06bd04e127bec9912deb4ba451fea7a1eca578328" @@ -313,7 +313,7 @@ name = "golang.org/x/crypto" packages = ["ssh/terminal"] pruneopts = "UT" - revision = "86ce3cb696783b739e41e834e2eead3e1b4aa3fb" + revision = "f7b00557c8c46a1ea4b035cae84f52028c2c0564" [[projects]] branch = "master" @@ -327,7 +327,7 @@ "html/charset", ] pruneopts = "UT" - revision = "16171245cfb220d5317888b716d69c1fb4e7992b" + revision = "244492dfa37ae2ce87222fd06250a03160745faa" [[projects]] branch = "master" @@ -343,14 +343,14 @@ [[projects]] branch = "master" - digest = "1:b40521580b68b3cd78f87d1820116ba94090f426b7da3c4e3124e874e1f431dd" + digest = "1:e32edd8d81fe0447cb6855e2d2b0549b63717539513e09b3a1e4bb20d48f46c6" name = "golang.org/x/sys" packages = [ "unix", "windows", ] pruneopts = "UT" - revision = "d101bd2416d505c0448a6ce8a282482678040a89" + revision = "5c8b2ff67527cb88b770f693cebf3799036d8bc0" [[projects]] digest = "1:7570a3e4daa14b7627089e77ad8c714f5f36b4cf1b7dfd8510df7d6935dc42a0" @@ -411,21 +411,21 @@ version = "v1.6.5" [[projects]] - digest = "1:abeb38ade3f32a92943e5be54f55ed6d6e3b6602761d74b4aab4c9dd45c18abd" + digest = "1:80057945464ffb5b0da1f026beb8df0e8dbd098eaf771a349291bed2cd29a83e" name = "gopkg.in/fsnotify.v1" packages = ["."] pruneopts = "UT" - revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9" + revision = "45d7d09e39ef4ac08d493309fa031790c15bfe8a" source = "https://github.com/fsnotify/fsnotify.git" - version = "v1.4.7" + version = "v1.4.9" [[projects]] - digest = "1:ef4bd1cd2563d93d3f48352fc24d499a4a2b54c11932814173653ece7db13800" + digest = "1:b9e2424fb01adfa88b03cf56d3ccd83bd3ce6a1a1b573031ca282178b6397d5a" name = "gopkg.in/ini.v1" packages = ["."] pruneopts = "UT" - revision = "32cf4f7e9c77f151e18b53067b55549b4d1d411d" - version = "v1.52.0" + revision = "8e0f5b3a15fc93a84881c6cc50129bd62e83a314" + version = "v1.54.0" [[projects]] branch = "v1" diff --git a/internal/cmd/binding/bind.go b/internal/cmd/binding/bind.go new file mode 100644 index 00000000..4e997a66 --- /dev/null +++ b/internal/cmd/binding/bind.go @@ -0,0 +1,118 @@ +/* + * Copyright 2018 The Service Manager Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package binding + +import ( + "github.com/Peripli/service-manager-cli/internal/cmd" + "github.com/Peripli/service-manager-cli/internal/output" + "github.com/Peripli/service-manager-cli/pkg/query" + "github.com/Peripli/service-manager-cli/pkg/types" + + "fmt" + "github.com/spf13/cobra" +) + +// BindCmd wraps the smctl bind command +type BindCmd struct { + *cmd.Context + + binding types.ServiceBinding + instanceName string + + outputFormat output.Format +} + +// NewBindCmd returns new bind command with context +func NewBindCmd(context *cmd.Context) *BindCmd { + return &BindCmd{Context: context, binding: types.ServiceBinding{}} +} + +// Prepare returns cobra command +func (bc *BindCmd) Prepare(prepare cmd.PrepareFunc) *cobra.Command { + result := &cobra.Command{ + Use: "bind [instance-name] [binding-name]", + Short: "Creates binding in SM", + Long: `Creates binding in SM`, + + PreRunE: prepare(bc, bc.Context), + RunE: cmd.RunE(bc), + } + + result.Flags().StringVarP(&bc.binding.ServiceInstanceID, "id", "", "", "ID of the service instance. Required when name is ambiguous") + cmd.AddFormatFlag(result.Flags()) + cmd.AddCommonQueryFlag(result.Flags(), &bc.Parameters) + cmd.AddModeFlag(result.Flags(), "async") + + return result +} + +// Validate validates command's arguments +func (bc *BindCmd) Validate(args []string) error { + if len(args) < 2 { + return fmt.Errorf("instance and binding names are required") + } + + bc.instanceName = args[0] + bc.binding.Name = args[1] + return nil +} + +// Run runs the command's logic +func (bc *BindCmd) Run() error { + if bc.binding.ServiceInstanceID == "" { + instanceToBind, err := bc.Client.ListInstances(&query.Parameters{ + FieldQuery: []string{ + fmt.Sprintf("name eq '%s'", bc.instanceName), + }, + }) + if err != nil { + return err + } + if len(instanceToBind.ServiceInstances) < 1 { + return fmt.Errorf("service instance with name %s not found", bc.instanceName) + } + if len(instanceToBind.ServiceInstances) > 1 { + return fmt.Errorf("more than one service instance with name %s found. Use --id flag to specify id of the instance to bind", bc.instanceName) + } + bc.binding.ServiceInstanceID = instanceToBind.ServiceInstances[0].ID + } + + resultBinding, location, err := bc.Client.Bind(&bc.binding, &bc.Parameters) + if err != nil { + return err + } + + if len(location) != 0 { + cmd.CommonHandleAsyncExecution(bc.Context, location, fmt.Sprintf("Service Binding %s successfully scheduled. To see status of the operation use:\n", bc.binding.Name)) + return nil + } + + resultBinding.ServiceInstanceName = bc.instanceName + output.PrintServiceManagerObject(bc.Output, bc.outputFormat, resultBinding) + output.Println(bc.Output) + return nil +} + +// SetOutputFormat set output format +func (bc *BindCmd) SetOutputFormat(format output.Format) { + bc.outputFormat = format +} + +// HideUsage hide command's usage +func (bc *BindCmd) HideUsage() bool { + return true +} diff --git a/internal/cmd/binding/bind_test.go b/internal/cmd/binding/bind_test.go new file mode 100644 index 00000000..c90051f8 --- /dev/null +++ b/internal/cmd/binding/bind_test.go @@ -0,0 +1,223 @@ +package binding + +import ( + "encoding/json" + "github.com/Peripli/service-manager/pkg/util" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "gopkg.in/yaml.v2" + "io/ioutil" + "net/http" + + "bytes" + "errors" + + "github.com/Peripli/service-manager-cli/internal/cmd" + "github.com/Peripli/service-manager-cli/pkg/smclient/smclientfakes" + "github.com/Peripli/service-manager-cli/pkg/types" + + "github.com/spf13/cobra" +) + +var _ = Describe("Bind Command test", func() { + var client *smclientfakes.FakeClient + var command *BindCmd + var buffer *bytes.Buffer + + var binding *types.ServiceBinding + var instance *types.ServiceInstance + + BeforeEach(func() { + buffer = &bytes.Buffer{} + client = &smclientfakes.FakeClient{} + context := &cmd.Context{Output: buffer, Client: client} + command = NewBindCmd(context) + }) + + validAsyncBindExecution := func(location string, args ...string) *cobra.Command { + instance = &types.ServiceInstance{ + ID: "instance-id", + Name: args[0], + } + binding = &types.ServiceBinding{ + ID: "instance-id", + Name: args[1], + } + operation := &types.Operation{ + State: "in progress", + } + client.StatusReturns(operation, nil) + client.ListInstancesReturns(&types.ServiceInstances{ServiceInstances: []types.ServiceInstance{*instance}}, nil) + client.BindReturns(binding, location, nil) + + bindCmd := command.Prepare(cmd.SmPrepare) + bindCmd.SetArgs(args) + Expect(bindCmd.Execute()).ToNot(HaveOccurred()) + + return bindCmd + } + + validSyncBindExecution := func(args ...string) *cobra.Command { + return validAsyncBindExecution("", append(args, "--mode", "sync")...) + } + + invalidBindCommandExecution := func(args ...string) error { + rpcCmd := command.Prepare(cmd.SmPrepare) + rpcCmd.SetArgs(args) + return rpcCmd.Execute() + } + + Describe("Valid request", func() { + Context("With necessary arguments provided", func() { + It("should be registered synchronously", func() { + validSyncBindExecution("instance-name", "binding-name") + + tableOutputExpected := binding.TableData().String() + + Expect(buffer.String()).To(ContainSubstring(tableOutputExpected)) + }) + + It("should print location when registered asynchronously", func() { + validAsyncBindExecution("location", "instance-name", "binding-name") + + Expect(buffer.String()).To(ContainSubstring(`smctl status location`)) + }) + + It("Argument values should be as expected", func() { + validSyncBindExecution("instance-name", "binding-name") + + Expect(command.binding.Name).To(Equal("binding-name")) + Expect(command.binding.ServiceInstanceID).To(Equal(instance.ID)) + }) + }) + + Context("With json output flag", func() { + It("should be printed in json output format", func() { + validSyncBindExecution("instance-name", "binding-name", "--output", "json") + + jsonByte, _ := json.MarshalIndent(binding, "", " ") + jsonOutputExpected := string(jsonByte) + "\n" + + Expect(buffer.String()).To(Equal(jsonOutputExpected)) + }) + }) + + Context("With yaml output flag", func() { + It("should be printed in yaml output format", func() { + validSyncBindExecution("instance-name", "binding-name", "--output", "yaml") + + yamlByte, _ := yaml.Marshal(binding) + yamlOutputExpected := string(yamlByte) + "\n" + + Expect(buffer.String()).To(Equal(yamlOutputExpected)) + }) + }) + + Context("With generic param flag", func() { + It("should pass it to SM", func() { + validSyncBindExecution("instance-name", "binding-name", "--param", "paramKey=paramValue") + + _, args := client.BindArgsForCall(0) + + Expect(args.GeneralParams).To(ConsistOf("paramKey=paramValue", "async=false")) + Expect(args.FieldQuery).To(BeEmpty()) + Expect(args.LabelQuery).To(BeEmpty()) + }) + }) + + Context("With sync flag", func() { + It("should pass it to SM", func() { + validSyncBindExecution("instance-name", "binding-name") + + _, args := client.BindArgsForCall(0) + + Expect(args.GeneralParams).To(ConsistOf("async=false")) + Expect(args.FieldQuery).To(BeEmpty()) + Expect(args.LabelQuery).To(BeEmpty()) + }) + }) + }) + + Describe("Invalid request", func() { + var instances *types.ServiceInstances + + BeforeEach(func() { + instances = &types.ServiceInstances{ + ServiceInstances: []types.ServiceInstance{ + {ID: "id", Name: "instance-name"}, + }, + } + }) + + JustBeforeEach(func() { + client.ListInstancesReturns(instances, nil) + }) + + Context("With not enough arguments provided", func() { + It("should return error", func() { + err := invalidBindCommandExecution("instance-name") + + Expect(err).Should(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("instance and binding names are required")) + }) + }) + + Context("When instance not found", func() { + BeforeEach(func() { + instances = &types.ServiceInstances{} + }) + + It("should return error", func() { + err := invalidBindCommandExecution("instance-name", "binding-name") + + Expect(err).Should(HaveOccurred()) + Expect(err.Error()).To(SatisfyAll(ContainSubstring("service instance with name"), ContainSubstring("not found"))) + }) + }) + + Context("when more than one instance with given name found", func() { + BeforeEach(func() { + instances.ServiceInstances = append(instances.ServiceInstances, types.ServiceInstance{ID: "456", Name: "instance-name"}) + }) + It("should return message", func() { + err := invalidBindCommandExecution("instance-name", "binding-name") + + Expect(err).Should(HaveOccurred()) + Expect(err.Error()).To(SatisfyAll(ContainSubstring("more than one service instance with name"), ContainSubstring("found. Use --id flag to specify id of the instance to bind"))) + }) + }) + + Context("With error from http client", func() { + It("should return error", func() { + client.BindReturns(nil, "", errors.New("Http Client Error")) + + err := invalidBindCommandExecution("instance-name", "binding-name") + + Expect(err).To(MatchError("Http Client Error")) + }) + }) + + Context("With http response error from http client", func() { + It("should return error's description", func() { + body := ioutil.NopCloser(bytes.NewReader([]byte("HTTP response error"))) + expectedError := util.HandleResponseError(&http.Response{Body: body}) + client.BindReturns(nil, "", expectedError) + + err := invalidBindCommandExecution("instance-name", "binding-name") + + Expect(err).Should(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("HTTP response error")) + }) + }) + + Context("With invalid output format", func() { + It("should return error", func() { + invFormat := "invalid-format" + err := invalidBindCommandExecution("instance-name", "binding-name", "--output", invFormat) + + Expect(err).Should(HaveOccurred()) + Expect(err.Error()).To(Equal("unknown output: " + invFormat)) + }) + }) + }) +}) diff --git a/internal/cmd/binding/binding_suite_test.go b/internal/cmd/binding/binding_suite_test.go new file mode 100644 index 00000000..b7a2cade --- /dev/null +++ b/internal/cmd/binding/binding_suite_test.go @@ -0,0 +1,12 @@ +package binding + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "testing" +) + +func TestBindingCmd(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "") +} diff --git a/internal/cmd/binding/get_binding.go b/internal/cmd/binding/get_binding.go index 998c5f0d..406d7e55 100644 --- a/internal/cmd/binding/get_binding.go +++ b/internal/cmd/binding/get_binding.go @@ -27,12 +27,11 @@ import ( "github.com/spf13/cobra" ) -// GetBindingCmd wraps the smctl list-brokers command +// GetBindingCmd wraps the smctl get-binding command type GetBindingCmd struct { *cmd.Context bindingName string - prepare cmd.PrepareFunc outputFormat output.Format } @@ -56,16 +55,17 @@ func (gb *GetBindingCmd) Run() error { return nil } - resultBindings := &types.ServiceBindings{} + resultBindings := &types.ServiceBindings{Vertical: true} for _, binding := range bindings.ServiceBindings { - bd, err := gb.Client.GetBindingByID(binding.ID, &query.Parameters{ - GeneralParams: []string{ - "last_op=true", - }, - }) + bd, err := gb.Client.GetBindingByID(binding.ID, &gb.Parameters) if err != nil { return err } + instance, err := gb.Client.GetInstanceByID(bd.ServiceInstanceID, &query.Parameters{}) + if err != nil { + return err + } + bd.ServiceInstanceName = instance.Name resultBindings.ServiceBindings = append(resultBindings.ServiceBindings, *bd) } @@ -98,13 +98,12 @@ func (gb *GetBindingCmd) HideUsage() bool { // Prepare returns cobra command func (gb *GetBindingCmd) Prepare(prepare cmd.PrepareFunc) *cobra.Command { - gb.prepare = prepare result := &cobra.Command{ Use: "get-binding [name]", Aliases: []string{"gsb"}, Short: "Get single binding", Long: `Get single binding by its name`, - PreRunE: gb.prepare(gb, gb.Context), + PreRunE: prepare(gb, gb.Context), RunE: cmd.RunE(gb), } diff --git a/internal/cmd/binding/get_binding_test.go b/internal/cmd/binding/get_binding_test.go index 94dcf891..cbdf7822 100644 --- a/internal/cmd/binding/get_binding_test.go +++ b/internal/cmd/binding/get_binding_test.go @@ -1,8 +1,6 @@ package binding import ( - "testing" - . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -13,24 +11,31 @@ import ( "github.com/Peripli/service-manager-cli/pkg/types" ) -func TestGetBindingCmd(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "") -} - var _ = Describe("Get binding command test", func() { var client *smclientfakes.FakeClient var command *GetBindingCmd var buffer *bytes.Buffer + + instance1 := types.ServiceInstance{ + ID: "1", + Name: "instance-name1", + } + + instance2 := types.ServiceInstance{ + ID: "2", + Name: "instance-name2", + } binding := types.ServiceBinding{ - Name: "binding1", - ServiceInstanceID: "1", - ID: "id1", + Name: "binding1", + ServiceInstanceID: "1", + ServiceInstanceName: "instance-name1", + ID: "id1", } binding2 := types.ServiceBinding{ - Name: "binding1", - ServiceInstanceID: "2", - ID: "id2", + Name: "binding1", + ServiceInstanceID: "2", + ServiceInstanceName: "instance-name2", + ID: "id2", } BeforeEach(func() { @@ -39,6 +44,8 @@ var _ = Describe("Get binding command test", func() { client.ListBindingsReturns(&types.ServiceBindings{ServiceBindings: []types.ServiceBinding{binding}}, nil) context := &cmd.Context{Output: buffer, Client: client} command = NewGetBindingCmd(context) + client.GetInstanceByIDReturnsOnCall(0, &instance1, nil) + client.GetInstanceByIDReturnsOnCall(1, &instance2, nil) }) executeWithArgs := func(args ...string) error { @@ -60,7 +67,7 @@ var _ = Describe("Get binding command test", func() { Context("when more than one binding with same name exists", func() { var response *types.ServiceBindings BeforeEach(func() { - response = &types.ServiceBindings{ServiceBindings: []types.ServiceBinding{binding, binding2}} + response = &types.ServiceBindings{ServiceBindings: []types.ServiceBinding{binding, binding2}, Vertical: true} client.ListBindingsReturns(response, nil) }) @@ -90,7 +97,7 @@ var _ = Describe("Get binding command test", func() { err := executeWithArgs("binding1") Expect(err).ShouldNot(HaveOccurred()) - result := &types.ServiceBindings{ServiceBindings: []types.ServiceBinding{binding}} + result := &types.ServiceBindings{ServiceBindings: []types.ServiceBinding{binding}, Vertical: true} Expect(buffer.String()).To(ContainSubstring(result.TableData().String())) }) }) diff --git a/internal/cmd/binding/list_bindings.go b/internal/cmd/binding/list_bindings.go index f1dad10d..5af7b6cf 100644 --- a/internal/cmd/binding/list_bindings.go +++ b/internal/cmd/binding/list_bindings.go @@ -19,6 +19,7 @@ package binding import ( "github.com/Peripli/service-manager-cli/internal/cmd" "github.com/Peripli/service-manager-cli/internal/output" + "github.com/Peripli/service-manager-cli/pkg/query" "github.com/spf13/cobra" ) @@ -41,6 +42,14 @@ func (li *ListBindingsCmd) Run() error { return err } + for i := range bindings.ServiceBindings { + instance, err := li.Client.GetInstanceByID(bindings.ServiceBindings[i].ServiceInstanceID, &query.Parameters{}) + if err != nil { + return err + } + bindings.ServiceBindings[i].ServiceInstanceName = instance.Name + } + output.PrintServiceManagerObject(li.Output, li.outputFormat, bindings) output.Println(li.Output) diff --git a/internal/cmd/binding/list_bindings_test.go b/internal/cmd/binding/list_bindings_test.go index d4bdc8c1..ddda0153 100644 --- a/internal/cmd/binding/list_bindings_test.go +++ b/internal/cmd/binding/list_bindings_test.go @@ -23,23 +23,26 @@ import ( "github.com/Peripli/service-manager-cli/internal/cmd" "github.com/Peripli/service-manager-cli/pkg/smclient/smclientfakes" "github.com/Peripli/service-manager-cli/pkg/types" - "gopkg.in/yaml.v2" - "testing" - . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + "gopkg.in/yaml.v2" ) -func TestListBindingsCmd(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "") -} - var _ = Describe("List bindings command test", func() { var client *smclientfakes.FakeClient var command *ListBindingsCmd var buffer *bytes.Buffer + instance1 := types.ServiceInstance{ + ID: "service_instance_id1", + Name: "instance-name1", + } + + instance2 := types.ServiceInstance{ + ID: "service_instance_id2", + Name: "instance-name2", + } + binding1 := types.ServiceBinding{ ID: "id1", Name: "binding1", @@ -57,6 +60,8 @@ var _ = Describe("List bindings command test", func() { client = &smclientfakes.FakeClient{} context := &cmd.Context{Output: buffer, Client: client} command = NewListBindingsCmd(context) + client.GetInstanceByIDReturnsOnCall(0, &instance1, nil) + client.GetInstanceByIDReturnsOnCall(1, &instance2, nil) }) executeWithArgs := func(args []string) error { diff --git a/internal/cmd/binding/unbind.go b/internal/cmd/binding/unbind.go new file mode 100644 index 00000000..0f54071e --- /dev/null +++ b/internal/cmd/binding/unbind.go @@ -0,0 +1,143 @@ +/* + * Copyright 2018 The Service Manager Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package binding + +import ( + "fmt" + "github.com/Peripli/service-manager-cli/internal/cmd" + "github.com/Peripli/service-manager-cli/internal/output" + "github.com/Peripli/service-manager-cli/pkg/query" + "github.com/spf13/cobra" + "io" +) + +// UnbindCmd wraps the smctl bind command +type UnbindCmd struct { + *cmd.Context + + input io.Reader + force bool + + instanceName string + bindingID string + bindingName string +} + +// NewUnbindCmd returns new bind command with context +func NewUnbindCmd(context *cmd.Context, input io.Reader) *UnbindCmd { + return &UnbindCmd{Context: context, input: input} +} + +// Prepare returns cobra command +func (ubc *UnbindCmd) Prepare(prepare cmd.PrepareFunc) *cobra.Command { + result := &cobra.Command{ + Use: "unbind [instance-name] [binding-name]", + Short: "Deletes a binding from SM", + Long: `Deletes a binding from SM`, + + PreRunE: prepare(ubc, ubc.Context), + RunE: cmd.RunE(ubc), + } + + result.Flags().StringVarP(&ubc.bindingID, "id", "", "", "ID of the service binding. Required when name is ambiguous") + result.Flags().BoolVarP(&ubc.force, "force", "f", false, "Force delete without confirmation") + cmd.AddCommonQueryFlag(result.Flags(), &ubc.Parameters) + cmd.AddModeFlag(result.Flags(), "async") + + return result +} + +// Validate validates command's arguments +func (ubc *UnbindCmd) Validate(args []string) error { + if ubc.bindingID != "" { + return nil + } + + if len(args) < 2 { + return fmt.Errorf("instance and binding names are required") + } + + ubc.instanceName = args[0] + ubc.bindingName = args[1] + return nil +} + +// Run runs the command's logic +func (ubc *UnbindCmd) Run() error { + if ubc.bindingID == "" { + instanceToUnbind, err := ubc.Client.ListInstances(&query.Parameters{ + FieldQuery: []string{ + fmt.Sprintf("name eq '%s'", ubc.instanceName), + }, + }) + if err != nil { + return err + } + if len(instanceToUnbind.ServiceInstances) < 1 { + return fmt.Errorf("service instance with name %s not found", ubc.instanceName) + } + if len(instanceToUnbind.ServiceInstances) > 1 { + return fmt.Errorf("more than one service instance with name %s found. Use --id flag to specify id of the binding to be deleted", ubc.instanceName) + } + + bindingsToDelete, err := ubc.Client.ListBindings(&query.Parameters{ + FieldQuery: []string{ + fmt.Sprintf("name eq '%s'", ubc.bindingName), + fmt.Sprintf("service_instance_id eq '%s'", instanceToUnbind.ServiceInstances[0].ID), + }, + }) + if err != nil { + return err + } + if len(bindingsToDelete.ServiceBindings) < 1 { + output.PrintMessage(ubc.Output, "Service Binding with name %s for instance with name %s not found", ubc.bindingName, ubc.instanceName) + return nil + } + ubc.bindingID = bindingsToDelete.ServiceBindings[0].ID + } + + location, err := ubc.Client.Unbind(ubc.bindingID, &ubc.Parameters) + if err != nil { + output.PrintMessage(ubc.Output, "Could not delete service binding. Reason: ") + return err + } + if len(location) != 0 { + cmd.CommonHandleAsyncExecution(ubc.Context, location, fmt.Sprintf("Service Binding %s successfully scheduled for deletion. To see status of the operation use:\n", ubc.bindingName)) + return nil + } + output.PrintMessage(ubc.Output, "Service Binding successfully deleted.\n") + return nil +} + +// AskForConfirmation asks the user to confirm deletion +func (ubc *UnbindCmd) AskForConfirmation() (bool, error) { + if !ubc.force { + message := fmt.Sprintf("Do you really want to delete binding with name [%s] for instance with name %s (Y/n): ", ubc.bindingName, ubc.instanceName) + return cmd.CommonConfirmationPrompt(message, ubc.Context, ubc.input) + } + return true, nil +} + +// PrintDeclineMessage prints confirmation decline message to the user +func (ubc *UnbindCmd) PrintDeclineMessage() { + cmd.CommonPrintDeclineMessage(ubc.Output) +} + +// HideUsage hide command's usage +func (ubc *UnbindCmd) HideUsage() bool { + return true +} diff --git a/internal/cmd/binding/unbind_test.go b/internal/cmd/binding/unbind_test.go new file mode 100644 index 00000000..bbac1ebb --- /dev/null +++ b/internal/cmd/binding/unbind_test.go @@ -0,0 +1,159 @@ +package binding + +import ( + "github.com/Peripli/service-manager/pkg/util" + "io/ioutil" + "net/http" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + "bytes" + + "github.com/Peripli/service-manager-cli/internal/cmd" + "github.com/Peripli/service-manager-cli/pkg/smclient/smclientfakes" + "github.com/Peripli/service-manager-cli/pkg/types" +) + +var _ = Describe("Unbind command test", func() { + var client *smclientfakes.FakeClient + var command *UnbindCmd + var buffer *bytes.Buffer + var promptBuffer *bytes.Buffer + var instances *types.ServiceInstances + var bindings *types.ServiceBindings + + BeforeEach(func() { + buffer = &bytes.Buffer{} + promptBuffer = &bytes.Buffer{} + client = &smclientfakes.FakeClient{} + context := &cmd.Context{Output: buffer, Client: client} + command = NewUnbindCmd(context, promptBuffer) + + instances = &types.ServiceInstances{} + instances.ServiceInstances = []types.ServiceInstance{{ID: "1234", Name: "instance-name"}} + bindings = &types.ServiceBindings{} + bindings.ServiceBindings = []types.ServiceBinding{{ID: "id", Name: "binding-name", ServiceInstanceID: "1234"}} + }) + + JustBeforeEach(func() { + client.ListInstancesReturns(instances, nil) + client.ListBindingsReturns(bindings, nil) + }) + + executeWithArgs := func(args ...string) error { + commandToRun := command.Prepare(cmd.SmPrepare) + commandToRun.SetArgs(args) + + return commandToRun.Execute() + } + + Context("when existing binding is being deleted forcefully", func() { + It("should list success message", func() { + client.UnbindReturns("", nil) + err := executeWithArgs("instance-name", "binding-name", "-f") + + Expect(err).ShouldNot(HaveOccurred()) + Expect(buffer.String()).To(ContainSubstring("Service Binding successfully deleted.")) + }) + }) + + Context("when existing binding is being deleted", func() { + It("should list success message when confirmed", func() { + client.UnbindReturns("", nil) + promptBuffer.WriteString("y") + err := executeWithArgs("instance-name", "binding-name") + + Expect(err).ShouldNot(HaveOccurred()) + Expect(buffer.String()).To(ContainSubstring("Service Binding successfully deleted.")) + }) + + It("should print delete declined when declined", func() { + client.UnbindReturns("", nil) + promptBuffer.WriteString("n") + err := executeWithArgs("instance-name", "binding-name") + + Expect(err).ShouldNot(HaveOccurred()) + Expect(buffer.String()).To(ContainSubstring("Delete declined")) + }) + }) + + Context("when generic parameter flag is used", func() { + It("should pass it to SM", func() { + client.UnbindReturns("", nil) + promptBuffer.WriteString("y") + param := "parameterKey=parameterValue" + err := executeWithArgs("instance-name", "binding-name", "--param", param) + Expect(err).ShouldNot(HaveOccurred()) + + _, args := client.UnbindArgsForCall(0) + + Expect(args.GeneralParams).To(ConsistOf(param, "async=true")) + Expect(args.FieldQuery).To(BeEmpty()) + Expect(args.LabelQuery).To(BeEmpty()) + }) + }) + + Context("With sync flag", func() { + It("should pass it to SM", func() { + client.UnbindReturns("", nil) + promptBuffer.WriteString("y") + + err := executeWithArgs("instance-name", "binding-name", "--mode", "sync") + Expect(err).ShouldNot(HaveOccurred()) + + _, args := client.UnbindArgsForCall(0) + + Expect(args.GeneralParams).To(ConsistOf("async=false")) + Expect(args.FieldQuery).To(BeEmpty()) + Expect(args.LabelQuery).To(BeEmpty()) + }) + }) + + Context("when non-existing bindings are being deleted", func() { + BeforeEach(func() { + bindings = &types.ServiceBindings{} + }) + It("should return message", func() { + err := executeWithArgs("instance-name", "non-existing-name", "-f") + + Expect(err).ShouldNot(HaveOccurred()) + Expect(buffer.String()).To(SatisfyAll(ContainSubstring("Service Binding"), ContainSubstring("not found"))) + }) + }) + + Context("when more than one instance with given name found", func() { + BeforeEach(func() { + instances.ServiceInstances = append(instances.ServiceInstances, types.ServiceInstance{ID: "456", Name: "instance-name"}) + }) + It("should return message", func() { + err := executeWithArgs("instance-name", "binding-name", "-f") + + Expect(err).Should(HaveOccurred()) + Expect(err.Error()).To(SatisfyAll(ContainSubstring("more than one service instance with name"), ContainSubstring("found. Use --id flag to specify id of the binding to be deleted"))) + }) + }) + + Context("when SM returns error", func() { + It("should return error message", func() { + body := ioutil.NopCloser(bytes.NewReader([]byte(""))) + expectedError := util.HandleResponseError(&http.Response{Body: body, StatusCode: http.StatusInternalServerError}) + client.UnbindReturns("", expectedError) + err := executeWithArgs("instance-name", "binding-name", "-f") + + Expect(err).Should(HaveOccurred()) + Expect(buffer.String()).To(ContainSubstring("Could not delete service binding. Reason:")) + + }) + }) + + Context("when no arguments are provided", func() { + It("should print required arguments", func() { + client.UnbindReturns("", nil) + err := executeWithArgs() + + Expect(err).Should(HaveOccurred()) + Expect(err).To(MatchError("instance and binding names are required")) + }) + }) +}) diff --git a/internal/cmd/broker/broker_suite_test.go b/internal/cmd/broker/broker_suite_test.go new file mode 100644 index 00000000..2a34bf36 --- /dev/null +++ b/internal/cmd/broker/broker_suite_test.go @@ -0,0 +1,12 @@ +package broker + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "testing" +) + +func TestBrokerCmd(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "") +} diff --git a/internal/cmd/broker/delete_broker.go b/internal/cmd/broker/delete_broker.go index 47891718..b95a284e 100644 --- a/internal/cmd/broker/delete_broker.go +++ b/internal/cmd/broker/delete_broker.go @@ -18,10 +18,9 @@ package broker import ( "fmt" - "io" - "strings" - "github.com/Peripli/service-manager-cli/internal/output" + "github.com/Peripli/service-manager-cli/pkg/query" + "io" "github.com/spf13/cobra" @@ -31,7 +30,6 @@ import ( // DeleteBrokerCmd wraps the smctl delete-broker command type DeleteBrokerCmd struct { *cmd.Context - prepare cmd.PrepareFunc input io.Reader force bool @@ -57,16 +55,28 @@ func (dbc *DeleteBrokerCmd) Validate(args []string) error { // Run runs the command's logic func (dbc *DeleteBrokerCmd) Run() error { - dbc.Parameters.FieldQuery = append(dbc.Parameters.FieldQuery, fmt.Sprintf("name eq '%s'", dbc.name)) - if err := dbc.Client.DeleteBrokers(&dbc.Parameters); err != nil { - if strings.Contains(err.Error(), "StatusCode: 404") { - output.PrintMessage(dbc.Output, "Service Broker(s) not found.\n") - return nil - } - output.PrintMessage(dbc.Output, "Could not delete broker(s). Reason: ") + toDeleteBrokers, err := dbc.Client.ListBrokers(&query.Parameters{ + FieldQuery: []string{ + fmt.Sprintf("name eq '%s'", dbc.name), + }, + }) + if err != nil { return err } - output.PrintMessage(dbc.Output, "Service Broker(s) successfully deleted.\n") + if len(toDeleteBrokers.Brokers) < 1 { + output.PrintMessage(dbc.Output, "Service Broker not found.\n") + return nil + } + location, err := dbc.Client.DeleteBroker(toDeleteBrokers.Brokers[0].ID, &dbc.Parameters) + if err != nil { + output.PrintMessage(dbc.Output, "Could not delete broker. Reason: ") + return err + } + if len(location) != 0 { + cmd.CommonHandleAsyncExecution(dbc.Context, location, fmt.Sprintf("Service Broker %s successfully scheduled for deletion. To see status of the operation use:\n", dbc.name)) + return nil + } + output.PrintMessage(dbc.Output, "Service Broker successfully deleted.\n") return nil } @@ -91,18 +101,18 @@ func (dbc *DeleteBrokerCmd) PrintDeclineMessage() { // Prepare returns cobra command func (dbc *DeleteBrokerCmd) Prepare(prepare cmd.PrepareFunc) *cobra.Command { - dbc.prepare = prepare result := &cobra.Command{ Use: "delete-broker [name]", Aliases: []string{"db"}, - Short: "Deletes brokers", - Long: `Delete one or more brokers with name.`, - PreRunE: dbc.prepare(dbc, dbc.Context), + Short: "Deletes broker", + Long: `Deletes broker by name.`, + PreRunE: prepare(dbc, dbc.Context), RunE: cmd.RunE(dbc), } result.Flags().BoolVarP(&dbc.force, "force", "f", false, "Force delete without confirmation") cmd.AddCommonQueryFlag(result.Flags(), &dbc.Parameters) + cmd.AddModeFlag(result.Flags(), "sync") return result } diff --git a/internal/cmd/broker/delete_broker_test.go b/internal/cmd/broker/delete_broker_test.go index 55c02076..c3e4ab63 100644 --- a/internal/cmd/broker/delete_broker_test.go +++ b/internal/cmd/broker/delete_broker_test.go @@ -1,11 +1,9 @@ package broker import ( + "github.com/Peripli/service-manager/pkg/util" "io/ioutil" "net/http" - "testing" - - "github.com/Peripli/service-manager/pkg/util" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -17,16 +15,12 @@ import ( "github.com/Peripli/service-manager-cli/pkg/types" ) -func TestDeleteBrokerCmd(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "") -} - var _ = Describe("Delete brokers command test", func() { var client *smclientfakes.FakeClient var command *DeleteBrokerCmd var buffer *bytes.Buffer var promptBuffer *bytes.Buffer + var brokers *types.Brokers BeforeEach(func() { buffer = &bytes.Buffer{} @@ -35,11 +29,15 @@ var _ = Describe("Delete brokers command test", func() { context := &cmd.Context{Output: buffer, Client: client} command = NewDeleteBrokerCmd(context, promptBuffer) - brokers := &types.Brokers{} + brokers = &types.Brokers{} brokers.Brokers = []types.Broker{{ID: "1234", Name: "broker-name"}, {ID: "456", Name: "broker2"}} }) - executeWithArgs := func(args []string) error { + JustBeforeEach(func() { + client.ListBrokersReturns(brokers, nil) + }) + + executeWithArgs := func(args ...string) error { commandToRun := command.Prepare(cmd.SmPrepare) commandToRun.SetArgs(args) @@ -48,28 +46,28 @@ var _ = Describe("Delete brokers command test", func() { Context("when existing broker is being deleted forcefully", func() { It("should list success message", func() { - client.DeleteBrokersReturns(nil) - err := executeWithArgs([]string{"broker-name", "-f"}) + client.DeleteBrokerReturns("", nil) + err := executeWithArgs("broker-name", "-f") Expect(err).ShouldNot(HaveOccurred()) - Expect(buffer.String()).To(ContainSubstring("Service Broker(s) successfully deleted.")) + Expect(buffer.String()).To(ContainSubstring("Service Broker successfully deleted.")) }) }) Context("when existing broker is being deleted", func() { It("should list success message when confirmed", func() { - client.DeleteBrokersReturns(nil) + client.DeleteBrokerReturns("", nil) promptBuffer.WriteString("y") - err := executeWithArgs([]string{"broker-name"}) + err := executeWithArgs("broker-name") Expect(err).ShouldNot(HaveOccurred()) - Expect(buffer.String()).To(ContainSubstring("Service Broker(s) successfully deleted.")) + Expect(buffer.String()).To(ContainSubstring("Service Broker successfully deleted.")) }) It("should print delete declined when declined", func() { - client.DeleteBrokersReturns(nil) + client.DeleteBrokerReturns("", nil) promptBuffer.WriteString("n") - err := executeWithArgs([]string{"broker-name"}) + err := executeWithArgs("broker-name") Expect(err).ShouldNot(HaveOccurred()) Expect(buffer.String()).To(ContainSubstring("Delete declined")) @@ -78,29 +76,45 @@ var _ = Describe("Delete brokers command test", func() { Context("when generic parameter flag is used", func() { It("should pass it to SM", func() { - client.DeleteBrokersReturns(nil) + client.DeleteBrokerReturns("", nil) promptBuffer.WriteString("y") param := "parameterKey=parameterValue" - err := executeWithArgs([]string{"broker-name", "--param", param}) + err := executeWithArgs("broker-name", "--param", param) + Expect(err).ShouldNot(HaveOccurred()) + + _, args := client.DeleteBrokerArgsForCall(0) + + Expect(args.GeneralParams).To(ConsistOf(param, "async=false")) + Expect(args.FieldQuery).To(BeEmpty()) + Expect(args.LabelQuery).To(BeEmpty()) + }) + }) + + Context("With async flag", func() { + It("should pass it to SM", func() { + client.DeleteBrokerReturns("", nil) + promptBuffer.WriteString("y") + + err := executeWithArgs("broker-name", "--mode", "async") Expect(err).ShouldNot(HaveOccurred()) - args := client.DeleteBrokersArgsForCall(0) + _, args := client.DeleteBrokerArgsForCall(0) - Expect(args.GeneralParams).To(ConsistOf(param)) - Expect(args.FieldQuery).To(ConsistOf("name eq 'broker-name'")) + Expect(args.GeneralParams).To(ConsistOf("async=true")) + Expect(args.FieldQuery).To(BeEmpty()) Expect(args.LabelQuery).To(BeEmpty()) }) }) Context("when non-existing brokers are being deleted", func() { + BeforeEach(func() { + brokers = &types.Brokers{} + }) It("should return message", func() { - body := ioutil.NopCloser(bytes.NewReader([]byte(""))) - expectedError := util.HandleResponseError(&http.Response{Body: body, StatusCode: http.StatusNotFound}) - client.DeleteBrokersReturns(expectedError) - err := executeWithArgs([]string{"non-existing-name", "-f"}) + err := executeWithArgs("non-existing-name", "-f") Expect(err).ShouldNot(HaveOccurred()) - Expect(buffer.String()).To(ContainSubstring("Service Broker(s) not found")) + Expect(buffer.String()).To(ContainSubstring("Service Broker not found")) }) }) @@ -108,19 +122,19 @@ var _ = Describe("Delete brokers command test", func() { It("should return error message", func() { body := ioutil.NopCloser(bytes.NewReader([]byte(""))) expectedError := util.HandleResponseError(&http.Response{Body: body, StatusCode: http.StatusInternalServerError}) - client.DeleteBrokersReturns(expectedError) - err := executeWithArgs([]string{"name", "-f"}) + client.DeleteBrokerReturns("", expectedError) + err := executeWithArgs("name", "-f") Expect(err).Should(HaveOccurred()) - Expect(buffer.String()).To(ContainSubstring("Could not delete broker(s). Reason:")) + Expect(buffer.String()).To(ContainSubstring("Could not delete broker. Reason:")) }) }) Context("when no arguments are provided", func() { It("should print required arguments", func() { - client.DeleteBrokersReturns(nil) - err := executeWithArgs([]string{}) + client.DeleteBrokerReturns("", nil) + err := executeWithArgs() Expect(err).Should(HaveOccurred()) Expect(err).To(MatchError("single [name] is required")) diff --git a/internal/cmd/broker/get_broker.go b/internal/cmd/broker/get_broker.go index 8b416d7b..17e3f33a 100644 --- a/internal/cmd/broker/get_broker.go +++ b/internal/cmd/broker/get_broker.go @@ -56,11 +56,7 @@ func (gb *GetBrokerCmd) Run() error { } id := brokers.Brokers[0].ID - broker, err := gb.Client.GetBrokerByID(id, &query.Parameters{ - GeneralParams: []string{ - "last_op=true", - }, - }) + broker, err := gb.Client.GetBrokerByID(id, &gb.Parameters) if err != nil { return err } diff --git a/internal/cmd/broker/get_broker_test.go b/internal/cmd/broker/get_broker_test.go index 559f8d29..fdac7cb1 100644 --- a/internal/cmd/broker/get_broker_test.go +++ b/internal/cmd/broker/get_broker_test.go @@ -1,8 +1,6 @@ package broker import ( - "testing" - . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -13,11 +11,6 @@ import ( "github.com/Peripli/service-manager-cli/pkg/types" ) -func TestGetBrokerCmd(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "") -} - var _ = Describe("Get broker command test", func() { var client *smclientfakes.FakeClient diff --git a/internal/cmd/broker/list_brokers_test.go b/internal/cmd/broker/list_brokers_test.go index cf3b54f4..c174e090 100644 --- a/internal/cmd/broker/list_brokers_test.go +++ b/internal/cmd/broker/list_brokers_test.go @@ -3,8 +3,6 @@ package broker import ( "encoding/json" "errors" - "testing" - . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "gopkg.in/yaml.v2" @@ -16,11 +14,6 @@ import ( "github.com/Peripli/service-manager-cli/pkg/types" ) -func TestListBrokersCmd(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "") -} - var _ = Describe("List brokers command test", func() { var client *smclientfakes.FakeClient diff --git a/internal/cmd/broker/register_broker.go b/internal/cmd/broker/register_broker.go index 22241d32..908a82a9 100644 --- a/internal/cmd/broker/register_broker.go +++ b/internal/cmd/broker/register_broker.go @@ -59,6 +59,7 @@ func (rbc *RegisterBrokerCmd) Prepare(prepare cmd.PrepareFunc) *cobra.Command { result.Flags().StringVarP(&rbc.basicString, "basic", "b", "", "Sets the username and password for basic authentication. Format is .") cmd.AddFormatFlag(result.Flags()) cmd.AddCommonQueryFlag(result.Flags(), &rbc.Parameters) + cmd.AddModeFlag(result.Flags(), "sync") return result } @@ -85,11 +86,15 @@ func (rbc *RegisterBrokerCmd) Validate(args []string) error { // Run runs the command's logic func (rbc *RegisterBrokerCmd) Run() error { - resultBroker, err := rbc.Client.RegisterBroker(&rbc.broker, &rbc.Parameters) + resultBroker, location, err := rbc.Client.RegisterBroker(&rbc.broker, &rbc.Parameters) if err != nil { return err } + if len(location) != 0 { + cmd.CommonHandleAsyncExecution(rbc.Context, location, fmt.Sprintf("Service Broker %s successfully scheduled for registration. To see status of the operation use:\n", rbc.broker.Name)) + return nil + } output.PrintServiceManagerObject(rbc.Output, rbc.outputFormat, resultBroker) output.Println(rbc.Output) return nil diff --git a/internal/cmd/broker/register_broker_test.go b/internal/cmd/broker/register_broker_test.go index cd3f871b..40af66eb 100644 --- a/internal/cmd/broker/register_broker_test.go +++ b/internal/cmd/broker/register_broker_test.go @@ -3,13 +3,11 @@ package broker import ( "encoding/json" "github.com/Peripli/service-manager/pkg/util" - "io/ioutil" - "net/http" - "testing" - . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "gopkg.in/yaml.v2" + "io/ioutil" + "net/http" "bytes" "errors" @@ -21,11 +19,6 @@ import ( "github.com/spf13/cobra" ) -func TestRegisterBrokerCmd(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "") -} - var _ = Describe("Register Broker Command test", func() { var client *smclientfakes.FakeClient var command *RegisterBrokerCmd @@ -39,13 +32,17 @@ var _ = Describe("Register Broker Command test", func() { command = NewRegisterBrokerCmd(context) }) - validRegisterBrokerExecution := func(args []string) *cobra.Command { + validAsyncRegisterBrokerExecution := func(location string, args ...string) *cobra.Command { broker = &types.Broker{ Name: args[0], URL: args[1], ID: "1234", } - client.RegisterBrokerReturns(broker, nil) + operation := &types.Operation{ + State: "in progress", + } + client.StatusReturns(operation, nil) + client.RegisterBrokerReturns(broker, location, nil) rbcCmd := command.Prepare(cmd.SmPrepare) rbcCmd.SetArgs(args) @@ -54,7 +51,11 @@ var _ = Describe("Register Broker Command test", func() { return rbcCmd } - invalidRegisterBrokerCommandExecution := func(args []string) error { + validSyncRegisterBrokerExecution := func(args ...string) *cobra.Command { + return validAsyncRegisterBrokerExecution("", args...) + } + + invalidRegisterBrokerCommandExecution := func(args ...string) error { rpcCmd := command.Prepare(cmd.SmPrepare) rpcCmd.SetArgs(args) return rpcCmd.Execute() @@ -62,16 +63,22 @@ var _ = Describe("Register Broker Command test", func() { Describe("Valid request", func() { Context("With necessary arguments provided and basic flag", func() { - It("should be registered", func() { - validRegisterBrokerExecution([]string{"broker-name", "http://broker.com", "--basic", "user:password"}) + It("should be registered synchronously", func() { + validSyncRegisterBrokerExecution("broker-name", "http://broker.com", "--basic", "user:password") tableOutputExpected := broker.TableData().String() Expect(buffer.String()).To(ContainSubstring(tableOutputExpected)) }) + It("should print location when registered asynchronously", func() { + validAsyncRegisterBrokerExecution("location", "broker-name", "http://broker.com", "--basic", "user:password", "--mode", "async") + + Expect(buffer.String()).To(ContainSubstring(`smctl status location`)) + }) + It("Argument values should be as expected", func() { - validRegisterBrokerExecution([]string{"broker-name", "http://broker.com", "--basic", "user:password"}) + validSyncRegisterBrokerExecution("broker-name", "http://broker.com", "--basic", "user:password") Expect(command.broker.Name).To(Equal("broker-name")) Expect(command.broker.URL).To(Equal("http://broker.com")) @@ -82,7 +89,7 @@ var _ = Describe("Register Broker Command test", func() { Context("With description provided", func() { It("should save description value as expected", func() { - validRegisterBrokerExecution([]string{"validName", "validType", "validDescription", "--basic", "user:password"}) + validSyncRegisterBrokerExecution("validName", "validType", "validDescription", "--basic", "user:password") Expect(command.broker.Description).To(Equal("validDescription")) }) @@ -90,7 +97,7 @@ var _ = Describe("Register Broker Command test", func() { Context("With json output flag", func() { It("should be printed in json output format", func() { - validRegisterBrokerExecution([]string{"validName", "validUrl", "--basic", "user:password", "--output", "json"}) + validSyncRegisterBrokerExecution("validName", "validUrl", "--basic", "user:password", "--output", "json") jsonByte, _ := json.MarshalIndent(broker, "", " ") jsonOutputExpected := string(jsonByte) + "\n" @@ -101,7 +108,7 @@ var _ = Describe("Register Broker Command test", func() { Context("With yaml output flag", func() { It("should be printed in yaml output format", func() { - validRegisterBrokerExecution([]string{"validName", "validUrl", "--basic", "user:password", "--output", "yaml"}) + validSyncRegisterBrokerExecution("validName", "validUrl", "--basic", "user:password", "--output", "yaml") yamlByte, _ := yaml.Marshal(broker) yamlOutputExpected := string(yamlByte) + "\n" @@ -112,11 +119,23 @@ var _ = Describe("Register Broker Command test", func() { Context("With generic param flag", func() { It("should pass it to SM", func() { - validRegisterBrokerExecution([]string{"validName", "validType", "validDescription", "--basic", "user:password", "--param", "paramKey=paramValue"}) + validSyncRegisterBrokerExecution("validName", "validType", "validDescription", "--basic", "user:password", "--param", "paramKey=paramValue") + + _, args := client.RegisterBrokerArgsForCall(0) + + Expect(args.GeneralParams).To(ConsistOf("paramKey=paramValue", "async=false")) + Expect(args.FieldQuery).To(BeEmpty()) + Expect(args.LabelQuery).To(BeEmpty()) + }) + }) + + Context("With async flag", func() { + It("should pass it to SM", func() { + validSyncRegisterBrokerExecution("validName", "validType", "validDescription", "--basic", "user:password", "--mode", "async") _, args := client.RegisterBrokerArgsForCall(0) - Expect(args.GeneralParams).To(ConsistOf("paramKey=paramValue")) + Expect(args.GeneralParams).To(ConsistOf("async=true")) Expect(args.FieldQuery).To(BeEmpty()) Expect(args.LabelQuery).To(BeEmpty()) }) @@ -126,7 +145,7 @@ var _ = Describe("Register Broker Command test", func() { Describe("Invalid request", func() { Context("With not enough arguments provided", func() { It("should return error", func() { - err := invalidRegisterBrokerCommandExecution([]string{"validName", "--basic", "user:password"}) + err := invalidRegisterBrokerCommandExecution("validName", "--basic", "user:password") Expect(err.Error()).To(ContainSubstring("name and URL are required")) }) @@ -134,7 +153,7 @@ var _ = Describe("Register Broker Command test", func() { Context("With invalid basic flag provided", func() { It("should return error", func() { - err := invalidRegisterBrokerCommandExecution([]string{"validName", "validType", "--basic", "invalidBasicFlag"}) + err := invalidRegisterBrokerCommandExecution("validName", "validType", "--basic", "invalidBasicFlag") Expect(err.Error()).To(ContainSubstring("basic string is invalid")) }) @@ -142,9 +161,9 @@ var _ = Describe("Register Broker Command test", func() { Context("With error from http client", func() { It("should return error", func() { - client.RegisterBrokerReturns(nil, errors.New("Http Client Error")) + client.RegisterBrokerReturns(nil, "", errors.New("Http Client Error")) - err := invalidRegisterBrokerCommandExecution([]string{"validName", "validType", "--basic", "user:password"}) + err := invalidRegisterBrokerCommandExecution("validName", "validType", "--basic", "user:password") Expect(err).To(MatchError("Http Client Error")) }) @@ -154,9 +173,9 @@ var _ = Describe("Register Broker Command test", func() { It("should return error's description", func() { body := ioutil.NopCloser(bytes.NewReader([]byte("HTTP response error"))) expectedError := util.HandleResponseError(&http.Response{Body: body}) - client.RegisterBrokerReturns(nil, expectedError) + client.RegisterBrokerReturns(nil, "", expectedError) - err := invalidRegisterBrokerCommandExecution([]string{"validName", "validType", "--basic", "user:password"}) + err := invalidRegisterBrokerCommandExecution("validName", "validType", "--basic", "user:password") Expect(err).Should(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("HTTP response error")) @@ -166,7 +185,7 @@ var _ = Describe("Register Broker Command test", func() { Context("With invalid output format", func() { It("should return error", func() { invFormat := "invalid-format" - err := invalidRegisterBrokerCommandExecution([]string{"validName", "validUrl", "--basic", "user:password", "--output", invFormat}) + err := invalidRegisterBrokerCommandExecution("validName", "validUrl", "--basic", "user:password", "--output", invFormat) Expect(err).Should(HaveOccurred()) Expect(err.Error()).To(Equal("unknown output: " + invFormat)) diff --git a/internal/cmd/broker/update_broker.go b/internal/cmd/broker/update_broker.go index 18fe8b96..66268f51 100644 --- a/internal/cmd/broker/update_broker.go +++ b/internal/cmd/broker/update_broker.go @@ -19,6 +19,7 @@ package broker import ( "encoding/json" "fmt" + "github.com/Peripli/service-manager-cli/pkg/query" "github.com/Peripli/service-manager-cli/internal/output" "github.com/spf13/cobra" @@ -62,8 +63,11 @@ func (ubc *UpdateBrokerCmd) Validate(args []string) error { // Run runs the command's logic func (ubc *UpdateBrokerCmd) Run() error { - ubc.Parameters.FieldQuery = append(ubc.Parameters.FieldQuery, fmt.Sprintf("name eq '%s'", ubc.name)) - toUpdateBrokers, err := ubc.Client.ListBrokers(&ubc.Parameters) + toUpdateBrokers, err := ubc.Client.ListBrokers(&query.Parameters{ + FieldQuery: []string{ + fmt.Sprintf("name eq '%s'", ubc.name), + }, + }) if err != nil { return err } @@ -71,14 +75,16 @@ func (ubc *UpdateBrokerCmd) Run() error { return fmt.Errorf("broker with name %s not found", ubc.name) } toUpdateBroker := toUpdateBrokers.Brokers[0] - result, err := ubc.Client.UpdateBroker(toUpdateBroker.ID, ubc.updatedBroker, &ubc.Parameters) + result, location, err := ubc.Client.UpdateBroker(toUpdateBroker.ID, ubc.updatedBroker, &ubc.Parameters) if err != nil { return err } - + if len(location) != 0 { + cmd.CommonHandleAsyncExecution(ubc.Context, location, fmt.Sprintf("Service Broker %s successfully scheduled for update. To see status of the operation use:\n", toUpdateBroker.Name)) + return nil + } output.PrintServiceManagerObject(ubc.Output, ubc.outputFormat, result) output.Println(ubc.Output) - return nil } @@ -102,6 +108,7 @@ smctl update-broker broker '{"name": "new-name", "description": "new-description cmd.AddFormatFlag(result.Flags()) cmd.AddCommonQueryFlag(result.Flags(), &ubc.Parameters) + cmd.AddModeFlag(result.Flags(), "sync") return result } diff --git a/internal/cmd/broker/update_broker_test.go b/internal/cmd/broker/update_broker_test.go index 69685656..1fd8fc4c 100644 --- a/internal/cmd/broker/update_broker_test.go +++ b/internal/cmd/broker/update_broker_test.go @@ -3,8 +3,6 @@ package broker import ( "encoding/json" "errors" - "testing" - "gopkg.in/yaml.v2" "bytes" @@ -17,11 +15,6 @@ import ( "github.com/Peripli/service-manager-cli/pkg/types" ) -func TestUpdateBrokerCmd(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "") -} - var _ = Describe("Update broker command test", func() { var client *smclientfakes.FakeClient @@ -36,7 +29,7 @@ var _ = Describe("Update broker command test", func() { command = NewUpdateBrokerCmd(context) }) - validUpdateBrokerExecution := func(args ...string) error { + validAsyncUpdateBrokerExecution := func(location string, args ...string) error { broker = types.Broker{ Name: "broker1", ID: "id", @@ -44,14 +37,23 @@ var _ = Describe("Update broker command test", func() { Description: "description", } brokers := &types.Brokers{Brokers: []types.Broker{broker}} + operation := &types.Operation{ + State: "in progress", + } + + client.StatusReturns(operation, nil) client.ListBrokersReturns(brokers, nil) _ = json.Unmarshal([]byte(args[1]), broker) - client.UpdateBrokerReturns(&broker, nil) + client.UpdateBrokerReturns(&broker, location, nil) ubCmd := command.Prepare(cmd.SmPrepare) ubCmd.SetArgs(args) return ubCmd.Execute() } + validSyncUpdateBrokerExecution := func(args ...string) error { + return validAsyncUpdateBrokerExecution("", args...) + } + invalidUpdateBrokerExecution := func(args ...string) error { ubCmd := command.Prepare(cmd.SmPrepare) ubCmd.SetArgs(args) @@ -62,14 +64,19 @@ var _ = Describe("Update broker command test", func() { Context("With necessary arguments provided", func() { It("broker should be updated", func() { - err := validUpdateBrokerExecution("broker1", `{"description":"newDescription"}`) + err := validSyncUpdateBrokerExecution("broker1", `{"description":"newDescription"}`) Expect(err).ShouldNot(HaveOccurred()) Expect(buffer.String()).To(ContainSubstring(broker.TableData().String())) }) + It("should print location when updated asynchronously", func() { + Expect(validAsyncUpdateBrokerExecution("location", "broker1", `{"description":"newDescription"}`)).ShouldNot(HaveOccurred()) + Expect(buffer.String()).To(ContainSubstring(`smctl status location`)) + }) + It("argument values should be as expected", func() { - err := validUpdateBrokerExecution("broker1", `{"description":"newDescription"}`) + err := validSyncUpdateBrokerExecution("broker1", `{"description":"newDescription"}`) id, broker, _ := client.UpdateBrokerArgsForCall(0) @@ -81,7 +88,7 @@ var _ = Describe("Update broker command test", func() { Context("With json format flag", func() { It("should be printed in json format", func() { - err := validUpdateBrokerExecution("broker1", `{"description":"newDescription"}`, "--output", "json") + err := validSyncUpdateBrokerExecution("broker1", `{"description":"newDescription"}`, "--output", "json") jsonByte, _ := json.MarshalIndent(broker, "", " ") jsonOutputExpected := string(jsonByte) + "\n" @@ -93,7 +100,7 @@ var _ = Describe("Update broker command test", func() { Context("With yaml format flag", func() { It("should be printed in yaml format", func() { - err := validUpdateBrokerExecution("broker1", `{"description":"newDescription"}`, "--output", "yaml") + err := validSyncUpdateBrokerExecution("broker1", `{"description":"newDescription"}`, "--output", "yaml") yamlByte, _ := yaml.Marshal(broker) yamlOutputExpected := string(yamlByte) + "\n" @@ -105,13 +112,26 @@ var _ = Describe("Update broker command test", func() { Context("With generic parameter flag provided", func() { It("should pass it to SM", func() { - err := validUpdateBrokerExecution("broker1", `{"description":"newDescription"}`, "--param", "paramKey=paramValue") + err := validSyncUpdateBrokerExecution("broker1", `{"description":"newDescription"}`, "--param", "paramKey=paramValue") + Expect(err).ShouldNot(HaveOccurred()) + + _, _, args := client.UpdateBrokerArgsForCall(0) + + Expect(args.GeneralParams).To(ConsistOf("paramKey=paramValue", "async=false")) + Expect(args.FieldQuery).To(BeEmpty()) + Expect(args.LabelQuery).To(BeEmpty()) + }) + }) + + Context("With async flag", func() { + It("should pass it to SM", func() { + err := validSyncUpdateBrokerExecution("broker1", `{"description":"newDescription"}`, "--mode", "async") Expect(err).ShouldNot(HaveOccurred()) _, _, args := client.UpdateBrokerArgsForCall(0) - Expect(args.GeneralParams).To(ConsistOf("paramKey=paramValue")) - Expect(args.FieldQuery).To(ConsistOf("name eq 'broker1'")) + Expect(args.GeneralParams).To(ConsistOf("async=true")) + Expect(args.FieldQuery).To(BeEmpty()) Expect(args.LabelQuery).To(BeEmpty()) }) }) @@ -150,7 +170,7 @@ var _ = Describe("Update broker command test", func() { expectedErr := errors.New("http client error") brokers := &types.Brokers{Brokers: []types.Broker{broker}} client.ListBrokersReturns(brokers, nil) - client.UpdateBrokerReturns(nil, expectedErr) + client.UpdateBrokerReturns(nil, "", expectedErr) err := invalidUpdateBrokerExecution("broker1", `{"description":"newDescription"}`) diff --git a/internal/cmd/commander.go b/internal/cmd/commander.go index fd191785..4cefca9a 100644 --- a/internal/cmd/commander.go +++ b/internal/cmd/commander.go @@ -34,6 +34,7 @@ import ( "github.com/Peripli/service-manager-cli/pkg/auth" "github.com/Peripli/service-manager-cli/pkg/auth/oidc" "github.com/Peripli/service-manager-cli/pkg/smclient" + "github.com/Peripli/service-manager/pkg/types" ) var supportedFormats = map[string]output.Format{ @@ -88,6 +89,14 @@ func SmPrepare(cmd Command, ctx *Context) func(*cobra.Command, []string) error { return err } + mode, err := c.Flags().GetString("mode") + if err == nil { + if mode != "async" && mode != "sync" { + return fmt.Errorf("only sync/async modes are supported") + } + ctx.Parameters.GeneralParams = append(ctx.Parameters.GeneralParams, fmt.Sprintf("async=%t", mode == "async")) + } + if ctx.Client == nil { settings, err := ctx.Configuration.Load() if err != nil { @@ -200,6 +209,29 @@ func AddCommonQueryFlag(flags *pflag.FlagSet, parameters *query.Parameters) { flags.StringArrayVarP(¶meters.GeneralParams, "param", "", nil, "Additional query parameters in the form key=value") } +// AddModeFlag adds the --mode flag for SM calls. +func AddModeFlag(flags *pflag.FlagSet, defValue string) { + flags.StringP("mode", "", defValue, "How calls to SM are performed sync or async") +} + +// CommonHandleAsyncExecution handles async execution of SM calls +func CommonHandleAsyncExecution(ctx *Context, location string, message string) { + operation, err := ctx.Client.Status(location, &query.Parameters{}) + if err != nil { + output.PrintMessage(ctx.Output, message) + output.PrintMessage(ctx.Output, "smctl status %s\n", location) + output.PrintMessage(ctx.Output, "Error while polling for operation: %s\n", err) + return + } + if operation.State != string(types.IN_PROGRESS) { + output.PrintServiceManagerObject(ctx.Output, output.FormatText, operation) + return + } + + output.PrintMessage(ctx.Output, message) + output.PrintMessage(ctx.Output, "smctl status %s\n", location) +} + //CommonConfirmationPrompt provides common logic for confirmation of an operation func CommonConfirmationPrompt(message string, ctx *Context, input io.Reader) (bool, error) { output.PrintMessage(ctx.Output, message) diff --git a/internal/cmd/instance/deprovision.go b/internal/cmd/instance/deprovision.go new file mode 100644 index 00000000..ca896bd6 --- /dev/null +++ b/internal/cmd/instance/deprovision.go @@ -0,0 +1,126 @@ +/* + * Copyright 2018 The Service Manager Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package instance + +import ( + "fmt" + "github.com/Peripli/service-manager-cli/internal/output" + "github.com/Peripli/service-manager-cli/pkg/query" + "io" + + "github.com/spf13/cobra" + + "github.com/Peripli/service-manager-cli/internal/cmd" +) + +// DeprovisionCmd wraps the smctl deprovision command +type DeprovisionCmd struct { + *cmd.Context + + input io.Reader + force bool + + name string + id string +} + +// NewDeprovisionCmd returns new deprovision command with context +func NewDeprovisionCmd(context *cmd.Context, input io.Reader) *DeprovisionCmd { + return &DeprovisionCmd{Context: context, input: input} +} + +// Validate validates command's arguments +func (dbc *DeprovisionCmd) Validate(args []string) error { + if len(args) != 1 { + return fmt.Errorf("single [name] is required") + } + + dbc.name = args[0] + + return nil +} + +// Run runs the command's logic +func (dbc *DeprovisionCmd) Run() error { + if dbc.id == "" { + toDeprovision, err := dbc.Client.ListInstances(&query.Parameters{ + FieldQuery: []string{ + fmt.Sprintf("name eq '%s'", dbc.name), + }, + }) + if err != nil { + return err + } + if len(toDeprovision.ServiceInstances) < 1 { + output.PrintMessage(dbc.Output, "Service Instance not found.\n") + return nil + } + if len(toDeprovision.ServiceInstances) > 1 { + return fmt.Errorf("more than one service instance with name %s found. Use --id flag to specify id of the instance to be deleted", dbc.name) + } + dbc.id = toDeprovision.ServiceInstances[0].ID + } + + location, err := dbc.Client.Deprovision(dbc.id, &dbc.Parameters) + if err != nil { + output.PrintMessage(dbc.Output, "Could not delete service instance. Reason: ") + return err + } + if len(location) != 0 { + cmd.CommonHandleAsyncExecution(dbc.Context, location, fmt.Sprintf("Service Instance %s successfully scheduled for deletion. To see status of the operation use:\n", dbc.name)) + return nil + } + output.PrintMessage(dbc.Output, "Service Instance successfully deleted.\n") + return nil +} + +// HideUsage hide command's usage +func (dbc *DeprovisionCmd) HideUsage() bool { + return true +} + +// AskForConfirmation asks the user to confirm deletion +func (dbc *DeprovisionCmd) AskForConfirmation() (bool, error) { + if !dbc.force { + message := fmt.Sprintf("Do you really want to delete instance with name [%s] (Y/n): ", dbc.name) + return cmd.CommonConfirmationPrompt(message, dbc.Context, dbc.input) + } + return true, nil +} + +// PrintDeclineMessage prints confirmation decline message to the user +func (dbc *DeprovisionCmd) PrintDeclineMessage() { + cmd.CommonPrintDeclineMessage(dbc.Output) +} + +// Prepare returns cobra command +func (dbc *DeprovisionCmd) Prepare(prepare cmd.PrepareFunc) *cobra.Command { + result := &cobra.Command{ + Use: "deprovision [name]", + Short: "Deletes service instance", + Long: `Deletes service instance by name.`, + PreRunE: prepare(dbc, dbc.Context), + RunE: cmd.RunE(dbc), + } + + result.Flags().BoolVarP(&dbc.force, "force", "f", false, "Force delete without confirmation") + result.Flags().StringVarP(&dbc.id, "id", "", "", "ID of the service instance. Required when name is ambiguous") + cmd.AddCommonQueryFlag(result.Flags(), &dbc.Parameters) + cmd.AddModeFlag(result.Flags(), "async") + + return result +} diff --git a/internal/cmd/instance/deprovision_test.go b/internal/cmd/instance/deprovision_test.go new file mode 100644 index 00000000..0fa4bf12 --- /dev/null +++ b/internal/cmd/instance/deprovision_test.go @@ -0,0 +1,155 @@ +package instance + +import ( + "github.com/Peripli/service-manager/pkg/util" + "io/ioutil" + "net/http" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + "bytes" + + "github.com/Peripli/service-manager-cli/internal/cmd" + "github.com/Peripli/service-manager-cli/pkg/smclient/smclientfakes" + "github.com/Peripli/service-manager-cli/pkg/types" +) + +var _ = Describe("Deprovision command test", func() { + var client *smclientfakes.FakeClient + var command *DeprovisionCmd + var buffer *bytes.Buffer + var promptBuffer *bytes.Buffer + var instances *types.ServiceInstances + + BeforeEach(func() { + buffer = &bytes.Buffer{} + promptBuffer = &bytes.Buffer{} + client = &smclientfakes.FakeClient{} + context := &cmd.Context{Output: buffer, Client: client} + command = NewDeprovisionCmd(context, promptBuffer) + + instances = &types.ServiceInstances{} + instances.ServiceInstances = []types.ServiceInstance{{ID: "1234", Name: "instance-name"}} + }) + + JustBeforeEach(func() { + client.ListInstancesReturns(instances, nil) + }) + + executeWithArgs := func(args ...string) error { + commandToRun := command.Prepare(cmd.SmPrepare) + commandToRun.SetArgs(args) + + return commandToRun.Execute() + } + + Context("when existing instance is being deleted forcefully", func() { + It("should list success message", func() { + client.DeprovisionReturns("", nil) + err := executeWithArgs("instance-name", "-f") + + Expect(err).ShouldNot(HaveOccurred()) + Expect(buffer.String()).To(ContainSubstring("Service Instance successfully deleted.")) + }) + }) + + Context("when existing instance is being deleted", func() { + It("should list success message when confirmed", func() { + client.DeprovisionReturns("", nil) + promptBuffer.WriteString("y") + err := executeWithArgs("instance-name") + + Expect(err).ShouldNot(HaveOccurred()) + Expect(buffer.String()).To(ContainSubstring("Service Instance successfully deleted.")) + }) + + It("should print delete declined when declined", func() { + client.DeprovisionReturns("", nil) + promptBuffer.WriteString("n") + err := executeWithArgs("instance-name") + + Expect(err).ShouldNot(HaveOccurred()) + Expect(buffer.String()).To(ContainSubstring("Delete declined")) + }) + }) + + Context("when generic parameter flag is used", func() { + It("should pass it to SM", func() { + client.DeprovisionReturns("", nil) + promptBuffer.WriteString("y") + param := "parameterKey=parameterValue" + err := executeWithArgs("instance-name", "--param", param) + Expect(err).ShouldNot(HaveOccurred()) + + _, args := client.DeprovisionArgsForCall(0) + + Expect(args.GeneralParams).To(ConsistOf(param, "async=true")) + Expect(args.FieldQuery).To(BeEmpty()) + Expect(args.LabelQuery).To(BeEmpty()) + }) + }) + + Context("With sync flag", func() { + It("should pass it to SM", func() { + client.DeprovisionReturns("", nil) + promptBuffer.WriteString("y") + + err := executeWithArgs("instance-name", "--mode", "sync") + Expect(err).ShouldNot(HaveOccurred()) + + _, args := client.DeprovisionArgsForCall(0) + + Expect(args.GeneralParams).To(ConsistOf("async=false")) + Expect(args.FieldQuery).To(BeEmpty()) + Expect(args.LabelQuery).To(BeEmpty()) + }) + }) + + Context("when non-existing instances are being deleted", func() { + BeforeEach(func() { + instances = &types.ServiceInstances{} + }) + It("should return message", func() { + err := executeWithArgs("non-existing-name", "-f") + + Expect(err).ShouldNot(HaveOccurred()) + Expect(buffer.String()).To(ContainSubstring("Service Instance not found")) + }) + }) + + Context("when more than one instance with given name found", func() { + BeforeEach(func() { + instances.ServiceInstances = append(instances.ServiceInstances, types.ServiceInstance{ID: "456", Name: "instance-name"}) + }) + It("should return message", func() { + err := executeWithArgs("instance-name", "-f") + + Expect(err).Should(HaveOccurred()) + Expect(err.Error()).To(SatisfyAll(ContainSubstring("more than one service instance with name"), ContainSubstring("found. Use --id flag to specify id of the instance to be deleted"))) + }) + }) + + Context("when SM returns error", func() { + It("should return error message", func() { + body := ioutil.NopCloser(bytes.NewReader([]byte(""))) + expectedError := util.HandleResponseError(&http.Response{Body: body, StatusCode: http.StatusInternalServerError}) + client.DeprovisionReturns("", expectedError) + err := executeWithArgs("name", "-f") + + Expect(err).Should(HaveOccurred()) + Expect(buffer.String()).To(ContainSubstring("Could not delete service instance. Reason:")) + + }) + }) + + Context("when no arguments are provided", func() { + It("should print required arguments", func() { + client.DeprovisionReturns("", nil) + err := executeWithArgs() + + Expect(err).Should(HaveOccurred()) + Expect(err).To(MatchError("single [name] is required")) + }) + }) +}) diff --git a/internal/cmd/instance/get_instance.go b/internal/cmd/instance/get_instance.go index e9bba35e..ab25e08e 100644 --- a/internal/cmd/instance/get_instance.go +++ b/internal/cmd/instance/get_instance.go @@ -32,7 +32,6 @@ type GetInstanceCmd struct { *cmd.Context instanceName string - prepare cmd.PrepareFunc outputFormat output.Format } @@ -56,13 +55,9 @@ func (gb *GetInstanceCmd) Run() error { return nil } - resultInstances := &types.ServiceInstances{} + resultInstances := &types.ServiceInstances{Vertical: true} for _, instance := range instances.ServiceInstances { - inst, err := gb.Client.GetInstanceByID(instance.ID, &query.Parameters{ - GeneralParams: []string{ - "last_op=true", - }, - }) + inst, err := gb.Client.GetInstanceByID(instance.ID, &gb.Parameters) if err != nil { return err } @@ -71,7 +66,6 @@ func (gb *GetInstanceCmd) Run() error { output.PrintServiceManagerObject(gb.Output, gb.outputFormat, resultInstances) output.Println(gb.Output) - return nil } @@ -98,13 +92,12 @@ func (gb *GetInstanceCmd) HideUsage() bool { // Prepare returns cobra command func (gb *GetInstanceCmd) Prepare(prepare cmd.PrepareFunc) *cobra.Command { - gb.prepare = prepare result := &cobra.Command{ Use: "get-instance [name]", - Aliases: []string{"gb"}, + Aliases: []string{"gi"}, Short: "Get single instance", Long: `Get single instance by its name`, - PreRunE: gb.prepare(gb, gb.Context), + PreRunE: prepare(gb, gb.Context), RunE: cmd.RunE(gb), } diff --git a/internal/cmd/instance/get_instance_test.go b/internal/cmd/instance/get_instance_test.go index 1e988e99..7707d288 100644 --- a/internal/cmd/instance/get_instance_test.go +++ b/internal/cmd/instance/get_instance_test.go @@ -1,8 +1,6 @@ package instance import ( - "testing" - . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -13,11 +11,6 @@ import ( "github.com/Peripli/service-manager-cli/pkg/types" ) -func TestGetInstanceCmd(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "") -} - var _ = Describe("Get instance command test", func() { var client *smclientfakes.FakeClient var command *GetInstanceCmd @@ -60,7 +53,7 @@ var _ = Describe("Get instance command test", func() { Context("when more than one instance with same name exists", func() { var response *types.ServiceInstances BeforeEach(func() { - response = &types.ServiceInstances{ServiceInstances: []types.ServiceInstance{instance, instance2}} + response = &types.ServiceInstances{ServiceInstances: []types.ServiceInstance{instance, instance2}, Vertical: true} client.ListInstancesReturns(response, nil) }) @@ -90,7 +83,7 @@ var _ = Describe("Get instance command test", func() { err := executeWithArgs("instance1") Expect(err).ShouldNot(HaveOccurred()) - result := &types.ServiceInstances{ServiceInstances: []types.ServiceInstance{instance}} + result := &types.ServiceInstances{ServiceInstances: []types.ServiceInstance{instance}, Vertical: true} Expect(buffer.String()).To(ContainSubstring(result.TableData().String())) }) }) diff --git a/internal/cmd/instance/instance_suite_test.go b/internal/cmd/instance/instance_suite_test.go new file mode 100644 index 00000000..a26d9df6 --- /dev/null +++ b/internal/cmd/instance/instance_suite_test.go @@ -0,0 +1,12 @@ +package instance + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "testing" +) + +func TestInstanceCmd(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "") +} diff --git a/internal/cmd/instance/list_instances_test.go b/internal/cmd/instance/list_instances_test.go index d00eea63..5b3ac810 100644 --- a/internal/cmd/instance/list_instances_test.go +++ b/internal/cmd/instance/list_instances_test.go @@ -23,18 +23,11 @@ import ( "github.com/Peripli/service-manager-cli/internal/cmd" "github.com/Peripli/service-manager-cli/pkg/smclient/smclientfakes" "github.com/Peripli/service-manager-cli/pkg/types" - "gopkg.in/yaml.v2" - "testing" - . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + "gopkg.in/yaml.v2" ) -func TestListInstancesCmd(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "") -} - var _ = Describe("List instances command test", func() { var client *smclientfakes.FakeClient diff --git a/internal/cmd/instance/provision.go b/internal/cmd/instance/provision.go new file mode 100644 index 00000000..17cffbea --- /dev/null +++ b/internal/cmd/instance/provision.go @@ -0,0 +1,159 @@ +/* + * Copyright 2018 The Service Manager Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package instance + +import ( + "encoding/json" + "github.com/Peripli/service-manager-cli/internal/cmd" + "github.com/Peripli/service-manager-cli/internal/output" + "github.com/Peripli/service-manager-cli/pkg/query" + "github.com/Peripli/service-manager-cli/pkg/types" + + "fmt" + "github.com/spf13/cobra" +) + +// ProvisionCmd wraps the smctl provision command +type ProvisionCmd struct { + *cmd.Context + + instance types.ServiceInstance + offeringName string + planName string + brokerName string + parametersJSON string + + outputFormat output.Format +} + +// NewProvisionCmd returns new provision command with context +func NewProvisionCmd(context *cmd.Context) *ProvisionCmd { + return &ProvisionCmd{Context: context, instance: types.ServiceInstance{}} +} + +// Prepare returns cobra command +func (pi *ProvisionCmd) Prepare(prepare cmd.PrepareFunc) *cobra.Command { + result := &cobra.Command{ + Use: "provision [name] [offering] [plan]", + Short: "Provisions an instance in SM", + Long: `Provisions an instance in SM`, + + PreRunE: prepare(pi, pi.Context), + RunE: cmd.RunE(pi), + } + + result.Flags().StringVarP(&pi.brokerName, "broker-name", "b", "", "Name of the broker which provides the service offering. Required when offering name is ambiguous") + result.Flags().StringVarP(&pi.parametersJSON, "parameters", "c", "", "Valid JSON object containing instance parameters") + cmd.AddFormatFlag(result.Flags()) + cmd.AddCommonQueryFlag(result.Flags(), &pi.Parameters) + cmd.AddModeFlag(result.Flags(), "async") + + return result +} + +// Validate validates command's arguments +func (pi *ProvisionCmd) Validate(args []string) error { + if len(args) < 3 { + return fmt.Errorf("name, offering and plan are required") + } + + pi.instance.Name = args[0] + pi.offeringName = args[1] + pi.planName = args[2] + + return nil +} + +// Run runs the command's logic +func (pi *ProvisionCmd) Run() error { + offerings, err := pi.Client.ListOfferings(&query.Parameters{ + FieldQuery: []string{ + fmt.Sprintf("name eq '%s'", pi.offeringName), + }, + }) + if err != nil { + return err + } + if len(offerings.ServiceOfferings) == 0 { + return fmt.Errorf("service offering with name %s not found", pi.offeringName) + } + + pi.instance.ServiceID = offerings.ServiceOfferings[0].ID + + if len(offerings.ServiceOfferings) > 1 { + if len(pi.brokerName) == 0 { + return fmt.Errorf("more than one service offering with name %s found. Use -b flag to specify broker name", pi.offeringName) + } + + brokers, err := pi.Client.ListBrokers(&query.Parameters{ + FieldQuery: []string{ + fmt.Sprintf("name eq '%s'", pi.brokerName), + }, + }) + if err != nil { + return err + } + if len(brokers.Brokers) != 1 { + return fmt.Errorf("exactly one broker with name %s expected, found %d", pi.brokerName, len(brokers.Brokers)) + } + for _, offering := range offerings.ServiceOfferings { + if offering.BrokerID == brokers.Brokers[0].ID { + pi.instance.ServiceID = offering.ID + break + } + } + } + + plans, err := pi.Client.ListPlans(&query.Parameters{ + FieldQuery: []string{ + fmt.Sprintf("name eq '%s'", pi.planName), + fmt.Sprintf("service_offering_id eq '%s'", pi.instance.ServiceID), + }, + }) + if err != nil { + return err + } + if len(plans.ServicePlans) != 1 { + return fmt.Errorf("exactly one service plan with name %s for offering with id %s expected", pi.planName, pi.instance.ServiceID) + } + + pi.instance.ServicePlanID = plans.ServicePlans[0].ID + pi.instance.Parameters = json.RawMessage(pi.parametersJSON) + + resultInstance, location, err := pi.Client.Provision(&pi.instance, &pi.Parameters) + if err != nil { + return err + } + + if len(location) != 0 { + cmd.CommonHandleAsyncExecution(pi.Context, location, fmt.Sprintf("Service Instance %s successfully scheduled for provisioning. To see status of the operation use:\n", pi.instance.Name)) + return nil + } + output.PrintServiceManagerObject(pi.Output, pi.outputFormat, resultInstance) + output.Println(pi.Output) + return nil +} + +// SetOutputFormat set output format +func (pi *ProvisionCmd) SetOutputFormat(format output.Format) { + pi.outputFormat = format +} + +// HideUsage hide command's usage +func (pi *ProvisionCmd) HideUsage() bool { + return true +} diff --git a/internal/cmd/instance/provision_test.go b/internal/cmd/instance/provision_test.go new file mode 100644 index 00000000..d5e131ed --- /dev/null +++ b/internal/cmd/instance/provision_test.go @@ -0,0 +1,261 @@ +package instance + +import ( + "encoding/json" + "github.com/Peripli/service-manager/pkg/util" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "gopkg.in/yaml.v2" + "io/ioutil" + "net/http" + + "bytes" + "errors" + + "github.com/Peripli/service-manager-cli/internal/cmd" + "github.com/Peripli/service-manager-cli/pkg/smclient/smclientfakes" + "github.com/Peripli/service-manager-cli/pkg/types" + + "github.com/spf13/cobra" +) + +const OfferingID = "offering_id" +const PlanID = "plan_id" + +var _ = Describe("Provision Command test", func() { + var client *smclientfakes.FakeClient + var command *ProvisionCmd + var buffer *bytes.Buffer + + var offerings *types.ServiceOfferings + var plans *types.ServicePlans + var instance *types.ServiceInstance + + BeforeEach(func() { + buffer = &bytes.Buffer{} + client = &smclientfakes.FakeClient{} + context := &cmd.Context{Output: buffer, Client: client} + command = NewProvisionCmd(context) + }) + + validAsyncProvisionExecution := func(location string, args ...string) *cobra.Command { + offerings = &types.ServiceOfferings{ + ServiceOfferings: []types.ServiceOffering{ + {ID: OfferingID, Name: args[1]}, + }, + } + plans = &types.ServicePlans{ + ServicePlans: []types.ServicePlan{ + {ID: PlanID, Name: args[2]}, + }, + } + instance = &types.ServiceInstance{ + Name: args[0], + } + operation := &types.Operation{ + State: "in progress", + } + client.StatusReturns(operation, nil) + client.ListOfferingsReturns(offerings, nil) + client.ListPlansReturns(plans, nil) + client.ProvisionReturns(instance, location, nil) + + piCmd := command.Prepare(cmd.SmPrepare) + piCmd.SetArgs(args) + Expect(piCmd.Execute()).ToNot(HaveOccurred()) + + return piCmd + } + + validSyncProvisionExecution := func(args ...string) *cobra.Command { + return validAsyncProvisionExecution("", append(args, "--mode", "sync")...) + } + + invalidProvisionCommandExecution := func(args ...string) error { + rpcCmd := command.Prepare(cmd.SmPrepare) + rpcCmd.SetArgs(args) + return rpcCmd.Execute() + } + + Describe("Valid request", func() { + Context("With necessary arguments provided", func() { + It("should be registered synchronously", func() { + validSyncProvisionExecution("instance-name", "offering-name", "plan-name") + + tableOutputExpected := instance.TableData().String() + + Expect(buffer.String()).To(ContainSubstring(tableOutputExpected)) + }) + + It("should print location when registered asynchronously", func() { + validAsyncProvisionExecution("location", "instance-name", "offering-name", "plan-name") + + Expect(buffer.String()).To(ContainSubstring(`smctl status location`)) + }) + + It("Argument values should be as expected", func() { + validSyncProvisionExecution("instance-name", "offering-name", "plan-name") + + Expect(command.instance.Name).To(Equal("instance-name")) + Expect(command.instance.ServiceID).To(Equal(OfferingID)) + Expect(command.instance.ServicePlanID).To(Equal(PlanID)) + }) + }) + + Context("With json output flag", func() { + It("should be printed in json output format", func() { + validSyncProvisionExecution("instance-name", "offering-name", "plan-name", "--output", "json") + + jsonByte, _ := json.MarshalIndent(instance, "", " ") + jsonOutputExpected := string(jsonByte) + "\n" + + Expect(buffer.String()).To(Equal(jsonOutputExpected)) + }) + }) + + Context("With yaml output flag", func() { + It("should be printed in yaml output format", func() { + validSyncProvisionExecution("instance-name", "offering-name", "plan-name", "--output", "yaml") + + yamlByte, _ := yaml.Marshal(instance) + yamlOutputExpected := string(yamlByte) + "\n" + + Expect(buffer.String()).To(Equal(yamlOutputExpected)) + }) + }) + + Context("With generic param flag", func() { + It("should pass it to SM", func() { + validSyncProvisionExecution("instance-name", "offering-name", "plan-name", "--param", "paramKey=paramValue") + + _, args := client.ProvisionArgsForCall(0) + + Expect(args.GeneralParams).To(ConsistOf("paramKey=paramValue", "async=false")) + Expect(args.FieldQuery).To(BeEmpty()) + Expect(args.LabelQuery).To(BeEmpty()) + }) + }) + + Context("With sync flag", func() { + It("should pass it to SM", func() { + validSyncProvisionExecution("instance-name", "offering-name", "plan-name") + + _, args := client.ProvisionArgsForCall(0) + + Expect(args.GeneralParams).To(ConsistOf("async=false")) + Expect(args.FieldQuery).To(BeEmpty()) + Expect(args.LabelQuery).To(BeEmpty()) + }) + }) + }) + + Describe("Invalid request", func() { + var offerings *types.ServiceOfferings + var plans *types.ServicePlans + var brokers *types.Brokers + + BeforeEach(func() { + offerings = &types.ServiceOfferings{ + ServiceOfferings: []types.ServiceOffering{ + {ID: OfferingID, Name: "offering-name", BrokerID: "broker-id"}, + }, + } + plans = &types.ServicePlans{ + ServicePlans: []types.ServicePlan{ + {ID: PlanID, Name: "plan-name"}, + }, + } + brokers = &types.Brokers{ + Brokers: []types.Broker{ + {ID: "broker-id", Name: "broker-name"}, + }, + } + }) + + JustBeforeEach(func() { + client.ListOfferingsReturns(offerings, nil) + client.ListPlansReturns(plans, nil) + client.ListBrokersReturns(brokers, nil) + }) + + Context("With not enough arguments provided", func() { + It("should return error", func() { + err := invalidProvisionCommandExecution("validName", "offering-name") + + Expect(err).Should(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("name, offering and plan are required")) + }) + }) + + Context("When offering not found", func() { + BeforeEach(func() { + offerings = &types.ServiceOfferings{} + }) + + It("should return error", func() { + err := invalidProvisionCommandExecution("validName", "offering-name", "plan-name") + + Expect(err).Should(HaveOccurred()) + Expect(err.Error()).To(SatisfyAll(ContainSubstring("service offering with name"), ContainSubstring("not found"))) + }) + }) + + Context("When more than one offering with same name found", func() { + BeforeEach(func() { + offerings.ServiceOfferings = append(offerings.ServiceOfferings, types.ServiceOffering{ID: OfferingID, Name: "offering-name"}) + }) + + Context("and broker name is not provided", func() { + It("should return error", func() { + err := invalidProvisionCommandExecution("validName", "offering-name", "plan-name") + + Expect(err).Should(HaveOccurred()) + Expect(err.Error()).To(SatisfyAll(ContainSubstring("more than one service offering with name"), ContainSubstring("found. Use -b flag to specify broker name"))) + }) + }) + + Context("and broker name is provided", func() { + It("should distinguish between offerings", func() { + client.ProvisionReturns(&types.ServiceInstance{}, "", nil) + + err := invalidProvisionCommandExecution("validName", "offering-name", "plan-name", "-b", "broker-name") + + Expect(err).ShouldNot(HaveOccurred()) + }) + }) + }) + + Context("With error from http client", func() { + It("should return error", func() { + client.ProvisionReturns(nil, "", errors.New("Http Client Error")) + + err := invalidProvisionCommandExecution("validName", "offering-name", "plan-name") + + Expect(err).To(MatchError("Http Client Error")) + }) + }) + + Context("With http response error from http client", func() { + It("should return error's description", func() { + body := ioutil.NopCloser(bytes.NewReader([]byte("HTTP response error"))) + expectedError := util.HandleResponseError(&http.Response{Body: body}) + client.ProvisionReturns(nil, "", expectedError) + + err := invalidProvisionCommandExecution("validName", "offering-name", "plan-name") + + Expect(err).Should(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("HTTP response error")) + }) + }) + + Context("With invalid output format", func() { + It("should return error", func() { + invFormat := "invalid-format" + err := invalidProvisionCommandExecution("validName", "offering-name", "plan-name", "--output", invFormat) + + Expect(err).Should(HaveOccurred()) + Expect(err.Error()).To(Equal("unknown output: " + invFormat)) + }) + }) + }) +}) diff --git a/internal/cmd/label/label.go b/internal/cmd/label/label.go index dffd0367..1c751095 100644 --- a/internal/cmd/label/label.go +++ b/internal/cmd/label/label.go @@ -6,7 +6,7 @@ import ( "github.com/Peripli/service-manager-cli/internal/cmd" "github.com/Peripli/service-manager-cli/internal/output" "github.com/Peripli/service-manager-cli/pkg/types" - "github.com/Peripli/service-manager/pkg/query" + smtypes "github.com/Peripli/service-manager/pkg/types" "github.com/Peripli/service-manager/pkg/web" "github.com/spf13/cobra" ) @@ -73,10 +73,10 @@ func (c *Cmd) Validate(args []string) error { return fmt.Errorf("unknown resource") } - labelChange := &query.LabelChange{} + labelChange := &smtypes.LabelChange{} if v, ok := operations[args[2]]; ok { - labelChange.Operation = query.LabelOperation(v) + labelChange.Operation = smtypes.LabelOperation(v) } else { return fmt.Errorf("unknown operation") } diff --git a/internal/cmd/label/label_test.go b/internal/cmd/label/label_test.go index 4fc6e07d..eb5ec30b 100644 --- a/internal/cmd/label/label_test.go +++ b/internal/cmd/label/label_test.go @@ -3,6 +3,7 @@ package label import ( "bytes" "errors" + smtypes "github.com/Peripli/service-manager/pkg/types" "testing" "io/ioutil" @@ -11,7 +12,6 @@ import ( "github.com/Peripli/service-manager-cli/internal/cmd" "github.com/Peripli/service-manager-cli/pkg/smclient/smclientfakes" "github.com/Peripli/service-manager-cli/pkg/types" - "github.com/Peripli/service-manager/pkg/query" "github.com/Peripli/service-manager/pkg/util" "github.com/Peripli/service-manager/pkg/web" . "github.com/onsi/ginkgo" @@ -60,7 +60,7 @@ var _ = Describe("Label Command test", func() { It("should pass arguments properly", func() { labelChanges = &types.LabelChanges{ - LabelChanges: []*query.LabelChange{{Key: "key", Operation: "add_values", Values: []string{"val1", "val2", "val3"}}}, + LabelChanges: []*smtypes.LabelChange{{Key: "key", Operation: "add_values", Values: []string{"val1", "val2", "val3"}}}, } err := validLabelExecution("platform", "id", "add-values", "key", "--val", "val1", "--val", "val2", "--val", "val3") diff --git a/internal/cmd/offering/list_offerings_test.go b/internal/cmd/offering/list_offerings_test.go index 797ea16a..f7c37bd5 100644 --- a/internal/cmd/offering/list_offerings_test.go +++ b/internal/cmd/offering/list_offerings_test.go @@ -23,18 +23,11 @@ import ( "github.com/Peripli/service-manager-cli/internal/cmd" "github.com/Peripli/service-manager-cli/pkg/smclient/smclientfakes" "github.com/Peripli/service-manager-cli/pkg/types" - "gopkg.in/yaml.v2" - "testing" - . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + "gopkg.in/yaml.v2" ) -func TestListOfferingsCmd(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "") -} - var _ = Describe("List offerings command test", func() { var client *smclientfakes.FakeClient diff --git a/internal/cmd/offering/marketplace_test.go b/internal/cmd/offering/marketplace_test.go index 710bb85b..f067fbd4 100644 --- a/internal/cmd/offering/marketplace_test.go +++ b/internal/cmd/offering/marketplace_test.go @@ -4,8 +4,6 @@ import ( "bytes" "encoding/json" "errors" - "testing" - "github.com/Peripli/service-manager-cli/internal/cmd" "github.com/Peripli/service-manager-cli/pkg/smclient/smclientfakes" "github.com/Peripli/service-manager-cli/pkg/types" @@ -14,11 +12,6 @@ import ( "gopkg.in/yaml.v2" ) -func TestMarketplaceCmd(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "") -} - var _ = Describe("Marketplace command test", func() { var client *smclientfakes.FakeClient var command *MarketplaceCmd diff --git a/internal/cmd/offering/offering_suite_test.go b/internal/cmd/offering/offering_suite_test.go new file mode 100644 index 00000000..74036151 --- /dev/null +++ b/internal/cmd/offering/offering_suite_test.go @@ -0,0 +1,12 @@ +package offering + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "testing" +) + +func TestOfferingCmd(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "") +} diff --git a/internal/cmd/platform/delete_platform_test.go b/internal/cmd/platform/delete_platform_test.go index c075000a..901cdac3 100644 --- a/internal/cmd/platform/delete_platform_test.go +++ b/internal/cmd/platform/delete_platform_test.go @@ -1,11 +1,9 @@ package platform import ( + "github.com/Peripli/service-manager/pkg/util" "io/ioutil" "net/http" - "testing" - - "github.com/Peripli/service-manager/pkg/util" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -17,11 +15,6 @@ import ( "github.com/Peripli/service-manager-cli/pkg/types" ) -func TestDeletePlatformCmd(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "") -} - var _ = Describe("Delete platforms command test", func() { var client *smclientfakes.FakeClient diff --git a/internal/cmd/platform/list_platforms_test.go b/internal/cmd/platform/list_platforms_test.go index d6051585..fa22e0b2 100644 --- a/internal/cmd/platform/list_platforms_test.go +++ b/internal/cmd/platform/list_platforms_test.go @@ -3,8 +3,6 @@ package platform import ( "encoding/json" "errors" - "testing" - . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "gopkg.in/yaml.v2" @@ -16,11 +14,6 @@ import ( "github.com/Peripli/service-manager-cli/pkg/types" ) -func TestListPlatformsCmd(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "") -} - var _ = Describe("List platforms command test", func() { var client *smclientfakes.FakeClient diff --git a/internal/cmd/platform/platform_suite_test.go b/internal/cmd/platform/platform_suite_test.go new file mode 100644 index 00000000..8e0443fc --- /dev/null +++ b/internal/cmd/platform/platform_suite_test.go @@ -0,0 +1,12 @@ +package platform + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "testing" +) + +func TestPlatformCmd(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "") +} diff --git a/internal/cmd/platform/register_platform_test.go b/internal/cmd/platform/register_platform_test.go index 389a374c..e60b2804 100644 --- a/internal/cmd/platform/register_platform_test.go +++ b/internal/cmd/platform/register_platform_test.go @@ -2,8 +2,6 @@ package platform import ( "encoding/json" - "testing" - "gopkg.in/yaml.v2" . "github.com/onsi/ginkgo" @@ -17,11 +15,6 @@ import ( "github.com/Peripli/service-manager-cli/pkg/types" ) -func TestRegisterPlatformCmd(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "") -} - var _ = Describe("Register Platform Command test", func() { var client *smclientfakes.FakeClient diff --git a/internal/cmd/platform/update_platform_test.go b/internal/cmd/platform/update_platform_test.go index 7fef3d5d..383dd6a2 100644 --- a/internal/cmd/platform/update_platform_test.go +++ b/internal/cmd/platform/update_platform_test.go @@ -3,8 +3,6 @@ package platform import ( "encoding/json" "errors" - "testing" - "gopkg.in/yaml.v2" "bytes" @@ -17,11 +15,6 @@ import ( "github.com/Peripli/service-manager-cli/pkg/types" ) -func TestUpdatePlatformCmd(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "") -} - var _ = Describe("Update platform command test", func() { var client *smclientfakes.FakeClient diff --git a/internal/cmd/status/status.go b/internal/cmd/status/status.go new file mode 100644 index 00000000..994b8850 --- /dev/null +++ b/internal/cmd/status/status.go @@ -0,0 +1,90 @@ +/* + * Copyright 2018 The Service Manager Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package status + +import ( + "fmt" + "github.com/Peripli/service-manager-cli/internal/cmd" + "github.com/Peripli/service-manager-cli/internal/output" + "github.com/spf13/cobra" + "strings" +) + +// Cmd wraps smctl status command +type Cmd struct { + *cmd.Context + + operationURL string + outputFormat output.Format +} + +// NewStatusCmd returns new label command with context +func NewStatusCmd(context *cmd.Context) *Cmd { + return &Cmd{Context: context} +} + +// Prepare returns cobra command +func (c *Cmd) Prepare(prepare cmd.PrepareFunc) *cobra.Command { + result := &cobra.Command{ + Use: "status [operation URL path]", + Short: "Get asynchronous operation's status", + Long: "Get asynchronous operation's status", + + PreRunE: prepare(c, c.Context), + RunE: cmd.RunE(c), + } + + cmd.AddFormatFlag(result.Flags()) + cmd.AddCommonQueryFlag(result.Flags(), &c.Parameters) + + return result +} + +// Validate validates command's arguments +func (c *Cmd) Validate(args []string) error { + if len(args) != 1 { + return fmt.Errorf("a path to operation is required") + } + c.operationURL = args[0] + return nil +} + +// Run runs the command's logic +func (c *Cmd) Run() error { + operation, err := c.Client.Status(c.operationURL, &c.Parameters) + if err != nil { + if strings.Contains(err.Error(), "StatusCode: 404") { + output.PrintMessage(c.Output, "Operation not found.\n") + return nil + } + return err + } + output.PrintServiceManagerObject(c.Output, c.outputFormat, operation) + output.Println(c.Output) + + return nil +} + +// HideUsage hide command's usage +func (c *Cmd) HideUsage() bool { + return true +} + +// SetOutputFormat set output format +func (c *Cmd) SetOutputFormat(format output.Format) { + c.outputFormat = format +} diff --git a/internal/cmd/status/status_test.go b/internal/cmd/status/status_test.go new file mode 100644 index 00000000..0b59a477 --- /dev/null +++ b/internal/cmd/status/status_test.go @@ -0,0 +1,80 @@ +package status + +import ( + "github.com/Peripli/service-manager/pkg/util" + "io/ioutil" + "net/http" + "testing" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + "bytes" + + "github.com/Peripli/service-manager-cli/internal/cmd" + "github.com/Peripli/service-manager-cli/pkg/smclient/smclientfakes" + "github.com/Peripli/service-manager-cli/pkg/types" +) + +func TestStatusCmd(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "") +} + +var _ = Describe("Status command test", func() { + + var client *smclientfakes.FakeClient + var command *Cmd + var buffer *bytes.Buffer + operation := &types.Operation{ + ID: "operation-id", + Type: "create", + State: "failed", + ResourceID: "broker-id", + } + + BeforeEach(func() { + buffer = &bytes.Buffer{} + client = &smclientfakes.FakeClient{} + context := &cmd.Context{Output: buffer, Client: client} + command = NewStatusCmd(context) + }) + + executeWithArgs := func(args ...string) error { + commandToRun := command.Prepare(cmd.SmPrepare) + commandToRun.SetArgs(args) + + return commandToRun.Execute() + } + + Context("when no operation url is provided", func() { + It("should return error", func() { + client.StatusReturns(operation, nil) + err := executeWithArgs() + + Expect(err).Should(HaveOccurred()) + }) + }) + + Context("when operation is not found", func() { + It("should return message", func() { + body := ioutil.NopCloser(bytes.NewReader([]byte(""))) + expectedError := util.HandleResponseError(&http.Response{Body: body, StatusCode: http.StatusNotFound}) + client.StatusReturns(nil, expectedError) + err := executeWithArgs("non-existing-path") + + Expect(err).ShouldNot(HaveOccurred()) + Expect(buffer.String()).To(ContainSubstring("Operation not found")) + }) + }) + + Context("when operation is found", func() { + It("should return its data", func() { + client.StatusReturns(operation, nil) + err := executeWithArgs("path") + + Expect(err).ShouldNot(HaveOccurred()) + Expect(buffer.String()).To(ContainSubstring(operation.TableData().String())) + }) + }) +}) diff --git a/internal/cmd/visibility/delete_visibility_test.go b/internal/cmd/visibility/delete_visibility_test.go index 5e9f7dad..661c310a 100644 --- a/internal/cmd/visibility/delete_visibility_test.go +++ b/internal/cmd/visibility/delete_visibility_test.go @@ -3,22 +3,15 @@ package visibility import ( "bytes" - "io/ioutil" - "net/http" - "testing" - "github.com/Peripli/service-manager-cli/internal/cmd" "github.com/Peripli/service-manager-cli/pkg/smclient/smclientfakes" "github.com/Peripli/service-manager/pkg/util" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + "io/ioutil" + "net/http" ) -func TestDeleteVisibilityCmd(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "") -} - var _ = Describe("Delete visibility command test", func() { var client *smclientfakes.FakeClient diff --git a/internal/cmd/visibility/list_visibilities_test.go b/internal/cmd/visibility/list_visibilities_test.go index 648692c1..14cd59a3 100644 --- a/internal/cmd/visibility/list_visibilities_test.go +++ b/internal/cmd/visibility/list_visibilities_test.go @@ -4,8 +4,6 @@ import ( "bytes" "encoding/json" "errors" - "testing" - "github.com/Peripli/service-manager-cli/internal/cmd" "github.com/Peripli/service-manager-cli/pkg/smclient/smclientfakes" "github.com/Peripli/service-manager-cli/pkg/types" @@ -14,11 +12,6 @@ import ( "gopkg.in/yaml.v2" ) -func TestListVisibilitiesCmd(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "") -} - var _ = Describe("List visibilities command test", func() { var client *smclientfakes.FakeClient diff --git a/internal/cmd/visibility/register_visibility_test.go b/internal/cmd/visibility/register_visibility_test.go index 9d40fe56..2efd5b72 100644 --- a/internal/cmd/visibility/register_visibility_test.go +++ b/internal/cmd/visibility/register_visibility_test.go @@ -4,8 +4,6 @@ import ( "bytes" "encoding/json" "errors" - "testing" - "github.com/Peripli/service-manager-cli/internal/cmd" "github.com/Peripli/service-manager-cli/pkg/smclient/smclientfakes" "github.com/Peripli/service-manager-cli/pkg/types" @@ -14,11 +12,6 @@ import ( "gopkg.in/yaml.v2" ) -func TestRegisterVisibilityCmd(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "") -} - var _ = Describe("Register visibility command test", func() { var client *smclientfakes.FakeClient diff --git a/internal/cmd/visibility/update_visibility_test.go b/internal/cmd/visibility/update_visibility_test.go index 73664240..b58b51bc 100644 --- a/internal/cmd/visibility/update_visibility_test.go +++ b/internal/cmd/visibility/update_visibility_test.go @@ -4,8 +4,6 @@ import ( "bytes" "encoding/json" "errors" - "testing" - "github.com/Peripli/service-manager-cli/internal/cmd" "github.com/Peripli/service-manager-cli/pkg/smclient/smclientfakes" "github.com/Peripli/service-manager-cli/pkg/types" @@ -14,11 +12,6 @@ import ( "gopkg.in/yaml.v2" ) -func TestUpdateVisibilityCmd(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "") -} - var _ = Describe("Update visibility command test", func() { var client *smclientfakes.FakeClient diff --git a/internal/cmd/visibility/visibility_suite_test.go b/internal/cmd/visibility/visibility_suite_test.go new file mode 100644 index 00000000..81743a75 --- /dev/null +++ b/internal/cmd/visibility/visibility_suite_test.go @@ -0,0 +1,12 @@ +package visibility + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "testing" +) + +func TestVisibilityCmd(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "") +} diff --git a/main.go b/main.go index d9241470..41ef1110 100644 --- a/main.go +++ b/main.go @@ -28,6 +28,7 @@ import ( "github.com/Peripli/service-manager-cli/internal/cmd/offering" "github.com/Peripli/service-manager-cli/internal/cmd/plan" "github.com/Peripli/service-manager-cli/internal/cmd/platform" + "github.com/Peripli/service-manager-cli/internal/cmd/status" "github.com/Peripli/service-manager-cli/internal/cmd/version" "github.com/Peripli/service-manager-cli/internal/cmd/visibility" "github.com/Peripli/service-manager-cli/pkg/auth" @@ -61,6 +62,8 @@ func main() { curl.NewCurlCmd(context, fs), binding.NewListBindingsCmd(context), binding.NewGetBindingCmd(context), + binding.NewBindCmd(context), + binding.NewUnbindCmd(context, os.Stdin), broker.NewRegisterBrokerCmd(context), broker.NewGetBrokerCmd(context), broker.NewListBrokersCmd(context), @@ -78,8 +81,11 @@ func main() { offering.NewMarketplaceCmd(context), plan.NewListPlansCmd(context), label.NewLabelCmd(context), + status.NewStatusCmd(context), instance.NewListInstancesCmd(context), instance.NewGetInstanceCmd(context), + instance.NewProvisionCmd(context), + instance.NewDeprovisionCmd(context, os.Stdin), }, PrepareFn: cmd.SmPrepare, } diff --git a/pkg/smclient/client.go b/pkg/smclient/client.go index 0b8fa696..b1f6b128 100644 --- a/pkg/smclient/client.go +++ b/pkg/smclient/client.go @@ -21,11 +21,10 @@ import ( "context" "encoding/json" "fmt" + "github.com/Peripli/service-manager/pkg/util" "io" "net/http" - "github.com/Peripli/service-manager/pkg/util" - "github.com/Peripli/service-manager/pkg/web" "github.com/Peripli/service-manager-cli/pkg/auth/oidc" @@ -46,27 +45,34 @@ type Client interface { UpdatePlatform(string, *types.Platform, *query.Parameters) (*types.Platform, error) DeletePlatforms(*query.Parameters) error - RegisterBroker(*types.Broker, *query.Parameters) (*types.Broker, error) - GetBrokerByID(id string, q *query.Parameters) (*types.Broker, error) + RegisterBroker(*types.Broker, *query.Parameters) (*types.Broker, string, error) + GetBrokerByID(string, *query.Parameters) (*types.Broker, error) ListBrokers(*query.Parameters) (*types.Brokers, error) - UpdateBroker(string, *types.Broker, *query.Parameters) (*types.Broker, error) - DeleteBrokers(*query.Parameters) error + UpdateBroker(string, *types.Broker, *query.Parameters) (*types.Broker, string, error) + DeleteBroker(string, *query.Parameters) (string, error) RegisterVisibility(*types.Visibility, *query.Parameters) (*types.Visibility, error) - ListOfferings(*query.Parameters) (*types.ServiceOfferings, error) - ListPlans(*query.Parameters) (*types.ServicePlans, error) ListVisibilities(*query.Parameters) (*types.Visibilities, error) UpdateVisibility(string, *types.Visibility, *query.Parameters) (*types.Visibility, error) DeleteVisibilities(*query.Parameters) error + ListOfferings(*query.Parameters) (*types.ServiceOfferings, error) + ListPlans(*query.Parameters) (*types.ServicePlans, error) + ListInstances(*query.Parameters) (*types.ServiceInstances, error) - GetInstanceByID(id string, q *query.Parameters) (*types.ServiceInstance, error) + GetInstanceByID(string, *query.Parameters) (*types.ServiceInstance, error) + Provision(*types.ServiceInstance, *query.Parameters) (*types.ServiceInstance, string, error) + Deprovision(string, *query.Parameters) (string, error) ListBindings(*query.Parameters) (*types.ServiceBindings, error) - GetBindingByID(id string, q *query.Parameters) (*types.ServiceBinding, error) + GetBindingByID(string, *query.Parameters) (*types.ServiceBinding, error) + Bind(*types.ServiceBinding, *query.Parameters) (*types.ServiceBinding, string, error) + Unbind(string, *query.Parameters) (string, error) Label(string, string, *types.LabelChanges, *query.Parameters) error + Status(string, *query.Parameters) (*types.Operation, error) + Marketplace(*query.Parameters) (*types.Marketplace, error) // Call makes HTTP request to the Service Manager server with authentication. @@ -140,7 +146,7 @@ func (client *serviceManagerClient) GetInfo(q *query.Parameters) (*types.Info, e // RegisterPlatform registers a platform in the service manager func (client *serviceManagerClient) RegisterPlatform(platform *types.Platform, q *query.Parameters) (*types.Platform, error) { var newPlatform *types.Platform - err := client.register(platform, web.PlatformsURL, q, &newPlatform) + _, err := client.register(platform, web.PlatformsURL, q, &newPlatform) if err != nil { return nil, err } @@ -148,42 +154,65 @@ func (client *serviceManagerClient) RegisterPlatform(platform *types.Platform, q } // RegisterBroker registers a broker in the service manager -func (client *serviceManagerClient) RegisterBroker(broker *types.Broker, q *query.Parameters) (*types.Broker, error) { +func (client *serviceManagerClient) RegisterBroker(broker *types.Broker, q *query.Parameters) (*types.Broker, string, error) { var newBroker *types.Broker - err := client.register(broker, web.ServiceBrokersURL, q, &newBroker) + location, err := client.register(broker, web.ServiceBrokersURL, q, &newBroker) if err != nil { - return nil, err + return nil, "", err } - return newBroker, nil + return newBroker, location, nil } // RegisterVisibility registers a visibility in the service manager func (client *serviceManagerClient) RegisterVisibility(visibility *types.Visibility, q *query.Parameters) (*types.Visibility, error) { var newVisibility *types.Visibility - err := client.register(visibility, web.VisibilitiesURL, q, &newVisibility) + _, err := client.register(visibility, web.VisibilitiesURL, q, &newVisibility) if err != nil { return nil, err } return newVisibility, nil } -func (client *serviceManagerClient) register(resource interface{}, url string, q *query.Parameters, result interface{}) error { +// Provision provisions a new service instance in service manager +func (client *serviceManagerClient) Provision(instance *types.ServiceInstance, q *query.Parameters) (*types.ServiceInstance, string, error) { + var newInstance *types.ServiceInstance + location, err := client.register(instance, web.ServiceInstancesURL, q, &newInstance) + if err != nil { + return nil, "", err + } + return newInstance, location, nil +} + +// Bind creates binding to an instance in service manager +func (client *serviceManagerClient) Bind(binding *types.ServiceBinding, q *query.Parameters) (*types.ServiceBinding, string, error) { + var newBinding *types.ServiceBinding + location, err := client.register(binding, web.ServiceBindingsURL, q, &newBinding) + if err != nil { + return nil, "", err + } + return newBinding, location, nil +} + +func (client *serviceManagerClient) register(resource interface{}, url string, q *query.Parameters, result interface{}) (string, error) { requestBody, err := json.Marshal(resource) if err != nil { - return err + return "", err } buffer := bytes.NewBuffer(requestBody) response, err := client.Call(http.MethodPost, url, buffer, q) if err != nil { - return err + return "", err } - if response.StatusCode != http.StatusCreated { - return util.HandleResponseError(response) + switch response.StatusCode { + case http.StatusCreated: + return "", httputil.UnmarshalResponse(response, &result) + case http.StatusAccepted: + return response.Header.Get("Location"), nil + default: + return "", util.HandleResponseError(response) } - - return httputil.UnmarshalResponse(response, &result) } // GetBrokerByID returns broker registered in the Service Manager satisfying provided queries @@ -303,8 +332,17 @@ func (client *serviceManagerClient) Marketplace(q *query.Parameters) (*types.Mar return marketplace, nil } +func (client *serviceManagerClient) Status(url string, q *query.Parameters) (*types.Operation, error) { + operation := &types.Operation{} + err := client.get(operation, url, &query.Parameters{ + GeneralParams: q.GeneralParams, + }) + + return operation, err +} + func (client *serviceManagerClient) list(result interface{}, url string, q *query.Parameters) error { - fullURL := httputil.NormalizeURL(client.config.URL) + buildURL(url, q) + fullURL := httputil.NormalizeURL(client.config.URL) + BuildURL(url, q) return util.ListAll(context.Background(), client.httpClient.Do, fullURL, result) } @@ -321,43 +359,55 @@ func (client *serviceManagerClient) get(result interface{}, url string, q *query return httputil.UnmarshalResponse(resp, &result) } -func (client *serviceManagerClient) DeleteBrokers(q *query.Parameters) error { - return client.delete(web.ServiceBrokersURL, q) +func (client *serviceManagerClient) DeleteBroker(id string, q *query.Parameters) (string, error) { + return client.delete(web.ServiceBrokersURL+"/"+id, q) } func (client *serviceManagerClient) DeletePlatforms(q *query.Parameters) error { - return client.delete(web.PlatformsURL, q) + _, err := client.delete(web.PlatformsURL, q) + return err } func (client *serviceManagerClient) DeleteVisibilities(q *query.Parameters) error { - return client.delete(web.VisibilitiesURL, q) + _, err := client.delete(web.VisibilitiesURL, q) + return err +} + +func (client *serviceManagerClient) Deprovision(id string, q *query.Parameters) (string, error) { + return client.delete(web.ServiceInstancesURL+"/"+id, q) } -func (client *serviceManagerClient) delete(url string, q *query.Parameters) error { +func (client *serviceManagerClient) Unbind(id string, q *query.Parameters) (string, error) { + return client.delete(web.ServiceBindingsURL+"/"+id, q) +} + +func (client *serviceManagerClient) delete(url string, q *query.Parameters) (string, error) { resp, err := client.Call(http.MethodDelete, url, nil, q) if err != nil { - return err + return "", err } - - if resp.StatusCode != http.StatusOK { - return util.HandleResponseError(resp) + switch resp.StatusCode { + case http.StatusOK: + return "", nil + case http.StatusAccepted: + return resp.Header.Get("Location"), nil + default: + return "", util.HandleResponseError(resp) } - - return nil } -func (client *serviceManagerClient) UpdateBroker(id string, updatedBroker *types.Broker, q *query.Parameters) (*types.Broker, error) { - result := &types.Broker{} - err := client.update(updatedBroker, web.ServiceBrokersURL, id, q, &result) +func (client *serviceManagerClient) UpdateBroker(id string, updatedBroker *types.Broker, q *query.Parameters) (*types.Broker, string, error) { + var result *types.Broker + location, err := client.update(updatedBroker, web.ServiceBrokersURL, id, q, &result) if err != nil { - return nil, err + return nil, "", err } - return result, nil + return result, location, nil } func (client *serviceManagerClient) UpdatePlatform(id string, updatedPlatform *types.Platform, q *query.Parameters) (*types.Platform, error) { result := &types.Platform{} - err := client.update(updatedPlatform, web.PlatformsURL, id, q, &result) + _, err := client.update(updatedPlatform, web.PlatformsURL, id, q, &result) if err != nil { return nil, err } @@ -366,29 +416,32 @@ func (client *serviceManagerClient) UpdatePlatform(id string, updatedPlatform *t func (client *serviceManagerClient) UpdateVisibility(id string, updatedVisibility *types.Visibility, q *query.Parameters) (*types.Visibility, error) { result := &types.Visibility{} - err := client.update(updatedVisibility, web.VisibilitiesURL, id, q, &result) + _, err := client.update(updatedVisibility, web.VisibilitiesURL, id, q, &result) if err != nil { return nil, err } return result, nil } -func (client *serviceManagerClient) update(resource interface{}, url string, id string, q *query.Parameters, result interface{}) error { +func (client *serviceManagerClient) update(resource interface{}, url string, id string, q *query.Parameters, result interface{}) (string, error) { requestBody, err := json.Marshal(resource) if err != nil { - return err + return "", err } buffer := bytes.NewBuffer(requestBody) resp, err := client.Call(http.MethodPatch, url+"/"+id, buffer, q) if err != nil { - return err + return "", err } - if resp.StatusCode != http.StatusOK { - return util.HandleResponseError(resp) + switch resp.StatusCode { + case http.StatusOK: + return "", httputil.UnmarshalResponse(resp, &result) + case http.StatusAccepted: + return resp.Header.Get("Location"), nil + default: + return "", util.HandleResponseError(resp) } - - return httputil.UnmarshalResponse(resp, &result) } func (client *serviceManagerClient) Label(url string, id string, change *types.LabelChanges, q *query.Parameters) error { @@ -410,7 +463,7 @@ func (client *serviceManagerClient) Label(url string, id string, change *types.L } func (client *serviceManagerClient) Call(method string, smpath string, body io.Reader, q *query.Parameters) (*http.Response, error) { - fullURL := httputil.NormalizeURL(client.config.URL) + buildURL(smpath, q) + fullURL := httputil.NormalizeURL(client.config.URL) + BuildURL(smpath, q) req, err := http.NewRequest(method, fullURL, body) if err != nil { @@ -430,7 +483,8 @@ func (client *serviceManagerClient) Call(method string, smpath string, body io.R return resp, nil } -func buildURL(baseURL string, q *query.Parameters) string { +// BuildURL builds the url with provided query parameters +func BuildURL(baseURL string, q *query.Parameters) string { queryParams := q.Encode() if queryParams == "" { return baseURL diff --git a/pkg/smclient/client_test.go b/pkg/smclient/client_test.go deleted file mode 100644 index c3266bb0..00000000 --- a/pkg/smclient/client_test.go +++ /dev/null @@ -1,1243 +0,0 @@ -package smclient - -import ( - "encoding/json" - "fmt" - "net/http" - "net/http/httptest" - "testing" - - cliquery "github.com/Peripli/service-manager-cli/pkg/query" - "github.com/Peripli/service-manager/pkg/query" - "github.com/Peripli/service-manager/pkg/web" - - "github.com/Peripli/service-manager-cli/pkg/types" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -type FakeAuthClient struct { - AccessToken string - requestURI string -} - -func (c *FakeAuthClient) Do(req *http.Request) (*http.Response, error) { - req.Header.Set("Authorization", "Bearer "+c.AccessToken) - c.requestURI = req.URL.RequestURI() - return http.DefaultClient.Do(req) -} - -type HandlerDetails struct { - Method string - Path string - ResponseBody []byte - ResponseStatusCode int -} - -func TestSMClient(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "") -} - -var _ = Describe("Service Manager Client test", func() { - var client Client - var handlerDetails []HandlerDetails - var validToken = "valid-token" - var invalidToken = "invalid-token" - var smServer *httptest.Server - var fakeAuthClient *FakeAuthClient - var params *cliquery.Parameters - - platform := &types.Platform{ - ID: "platformID", - Name: "cfeu10", - Type: "cf", - Description: "Test platform", - } - - broker := &types.Broker{ - Name: "test-broker", - URL: "http://test-url.com", - Credentials: &types.Credentials{Basic: types.Basic{User: "test user", Password: "test password"}}, - } - - initialOffering := &types.ServiceOffering{ - ID: "offeringID", - Name: "initial-offering", - Description: "Some description", - BrokerID: "id", - } - - plan := &types.ServicePlan{ - ID: "planID", - Name: "plan-1", - Description: "Sample Plan", - ServiceOfferingID: "offeringID", - } - - resultOffering := &types.ServiceOffering{ - ID: "offeringID", - Name: "initial-offering", - Description: "Some description", - Plans: []types.ServicePlan{*plan}, - BrokerID: "id", - BrokerName: "test-broker", - } - - visibility := &types.Visibility{ - ID: "visibilityID", - PlatformID: "platformID", - ServicePlanID: "planID", - } - - instance := &types.ServiceInstance{ - ID: "instanceID", - Name: "instance1", - ServicePlanID: "service_plan_id", - PlatformID: "platform_id", - } - - binding := &types.ServiceBinding{ - ID: "instanceID", - Name: "instance1", - ServiceInstanceID: "service_instance_id", - } - - labelChanges := &types.LabelChanges{ - LabelChanges: []*query.LabelChange{ - {Key: "key", Operation: query.LabelOperation("add"), Values: []string{"val1", "val2"}}, - }, - } - - createSMHandler := func() http.Handler { - mux := http.NewServeMux() - for i := range handlerDetails { - v := handlerDetails[i] - mux.HandleFunc(v.Path, func(response http.ResponseWriter, req *http.Request) { - if v.Method != req.Method { - return - } - authorization := req.Header.Get("Authorization") - if authorization != "Bearer "+validToken { - response.WriteHeader(http.StatusUnauthorized) - response.Write([]byte("")) - return - } - response.WriteHeader(v.ResponseStatusCode) - response.Write(v.ResponseBody) - }) - } - return mux - } - - verifyErrorMsg := func(errorMsg, path string, body []byte, statusCode int) { - Expect(errorMsg).To(ContainSubstring(buildURL(smServer.URL+path, params))) - Expect(errorMsg).To(ContainSubstring(string(body))) - Expect(errorMsg).To(ContainSubstring(fmt.Sprintf("StatusCode: %d", statusCode))) - } - - BeforeEach(func() { - params = &cliquery.Parameters{ - GeneralParams: []string{"key=value"}, - } - }) - - AfterEach(func() { - Expect(fakeAuthClient.requestURI).Should(ContainSubstring("key=value"), fmt.Sprintf("Request URI %s should contain ?key=value", fakeAuthClient.requestURI)) - }) - - JustBeforeEach(func() { - smServer = httptest.NewServer(createSMHandler()) - fakeAuthClient = &FakeAuthClient{AccessToken: validToken} - client = NewClient(fakeAuthClient, smServer.URL) - }) - - Describe("Get Info", func() { - BeforeEach(func() { - responseBody := []byte(`{"token_issuer_url": "http://uaa.com"}`) - handlerDetails = []HandlerDetails{ - {Method: http.MethodGet, Path: web.InfoURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, - } - }) - - Context("with general parameter", func() { - It("should make request with these parameters", func() { - info, err := client.GetInfo(params) - Expect(err).ToNot(HaveOccurred()) - Expect(info).ToNot(BeNil()) - }) - }) - }) - - Describe("Test failing client authentication", func() { - Context("When wrong token is used", func() { - BeforeEach(func() { - handlerDetails = []HandlerDetails{ - {Method: http.MethodGet, Path: web.ServiceBrokersURL}, - } - }) - It("should fail to authentication", func() { - fakeAuthClient.AccessToken = invalidToken - client = NewClient(fakeAuthClient, smServer.URL) - _, err := client.ListBrokers(params) - - Expect(err).Should(HaveOccurred()) - verifyErrorMsg(err.Error(), handlerDetails[0].Path, []byte{}, http.StatusUnauthorized) - }) - }) - }) - - Describe("Register platform", func() { - Context("When valid platform is being registered", func() { - BeforeEach(func() { - responseBody, _ := json.Marshal(platform) - handlerDetails = []HandlerDetails{ - {Method: http.MethodPost, Path: web.PlatformsURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusCreated}, - } - }) - It("should register successfully", func() { - responsePlatform, err := client.RegisterPlatform(platform, params) - - Expect(err).ShouldNot(HaveOccurred()) - Expect(responsePlatform).To(Equal(platform)) - }) - }) - - Context("When invalid platform is returned by SM", func() { - BeforeEach(func() { - responseBody, _ := json.Marshal(struct { - Name bool `json:"name"` - }{ - Name: true, - }) - handlerDetails = []HandlerDetails{ - {Method: http.MethodPost, Path: web.PlatformsURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusCreated}, - } - }) - It("should return error", func() { - responsePlatform, err := client.RegisterPlatform(platform, params) - - Expect(err).Should(HaveOccurred()) - Expect(responsePlatform).To(BeNil()) - }) - }) - - Context("When invalid status code is returned by SM", func() { - Context("And status code is successful", func() { - BeforeEach(func() { - responseBody, _ := json.Marshal(platform) - handlerDetails = []HandlerDetails{ - {Method: http.MethodPost, Path: web.PlatformsURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, - } - }) - It("should return error with status code", func() { - responsePlatform, err := client.RegisterPlatform(platform, params) - - Expect(err).Should(HaveOccurred()) - verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) - Expect(responsePlatform).To(BeNil()) - }) - }) - - Context("And status code is unsuccessful", func() { - BeforeEach(func() { - responseBody := []byte(`{ "description": "error"}`) - handlerDetails = []HandlerDetails{ - {Method: http.MethodPost, Path: web.PlatformsURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusBadRequest}, - } - }) - It("should return error with url and description", func() { - responsePlatform, err := client.RegisterPlatform(platform, params) - - Expect(err).Should(HaveOccurred()) - verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) - Expect(responsePlatform).To(BeNil()) - }) - }) - - Context("And response body is invalid", func() { - BeforeEach(func() { - responseBody := []byte(`{ "description": description", "error": "error"}`) - handlerDetails = []HandlerDetails{ - {Method: http.MethodPost, Path: web.PlatformsURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusBadRequest}, - } - }) - It("should return error without url and description if invalid response body", func() { - responsePlatform, err := client.RegisterPlatform(platform, params) - - Expect(err).Should(HaveOccurred()) - verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) - Expect(responsePlatform).To(BeNil()) - }) - }) - }) - - Context("When invalid config is set", func() { - It("should return error", func() { - client = NewClient(fakeAuthClient, "invalidURL") - _, err := client.RegisterPlatform(platform, params) - - Expect(err).Should(HaveOccurred()) - }) - }) - }) - - Describe("Register Visibility", func() { - Context("When valid visibility is being registered", func() { - BeforeEach(func() { - responseBody, _ := json.Marshal(visibility) - handlerDetails = []HandlerDetails{ - {Method: http.MethodPost, Path: web.VisibilitiesURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusCreated}, - } - }) - It("should register successfully", func() { - responseVisibility, err := client.RegisterVisibility(visibility, params) - - Expect(err).ShouldNot(HaveOccurred()) - Expect(responseVisibility).To(Equal(visibility)) - }) - }) - - Context("When invalid visibility is returned by SM", func() { - BeforeEach(func() { - responseBody, _ := json.Marshal(struct { - ID bool `json:"id"` - }{ - ID: true, - }) - handlerDetails = []HandlerDetails{ - {Method: http.MethodPost, Path: web.VisibilitiesURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusCreated}, - } - }) - - It("should return error", func() { - responseVisibility, err := client.RegisterVisibility(visibility, params) - - Expect(err).Should(HaveOccurred()) - Expect(responseVisibility).To(BeNil()) - }) - }) - - Context("When invalid status code is returned by SM", func() { - Context("And status code is successful", func() { - BeforeEach(func() { - responseBody, _ := json.Marshal(visibility) - handlerDetails = []HandlerDetails{ - {Method: http.MethodPost, Path: web.VisibilitiesURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, - } - }) - It("should return error with status code", func() { - responseVisibility, err := client.RegisterVisibility(visibility, params) - - Expect(err).Should(HaveOccurred()) - verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) - Expect(responseVisibility).To(BeNil()) - }) - }) - - Context("And status code is unsuccessful", func() { - BeforeEach(func() { - responseBody := []byte(`{ "description": "error"}`) - handlerDetails = []HandlerDetails{ - {Method: http.MethodPost, Path: web.VisibilitiesURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusBadRequest}, - } - }) - It("should return error with url and description", func() { - responseVisibility, err := client.RegisterVisibility(visibility, params) - - Expect(err).Should(HaveOccurred()) - verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) - Expect(responseVisibility).To(BeNil()) - }) - }) - - Context("And response body is invalid", func() { - BeforeEach(func() { - responseBody := []byte(`{ "description": description", "error": "error"}`) - handlerDetails = []HandlerDetails{ - {Method: http.MethodPost, Path: web.VisibilitiesURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusBadRequest}, - } - }) - It("should return error without url and description if invalid response body", func() { - responseVisibility, err := client.RegisterVisibility(visibility, params) - - Expect(err).Should(HaveOccurred()) - verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) - Expect(responseVisibility).To(BeNil()) - }) - }) - - }) - - Context("When invalid config is set", func() { - It("should return error", func() { - client = NewClient(fakeAuthClient, "invalidURL") - _, err := client.RegisterVisibility(visibility, params) - - Expect(err).Should(HaveOccurred()) - }) - }) - }) - - Describe("Register broker", func() { - Context("When valid broker is being registered", func() { - BeforeEach(func() { - responseBody, _ := json.Marshal(broker) - handlerDetails = []HandlerDetails{ - {Method: http.MethodPost, Path: web.ServiceBrokersURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusCreated}, - } - }) - It("should register successfully", func() { - responseBroker, err := client.RegisterBroker(broker, params) - - Expect(err).ShouldNot(HaveOccurred()) - Expect(responseBroker).To(Equal(broker)) - }) - }) - - Context("When invalid broker is being returned by SM", func() { - BeforeEach(func() { - responseBody, _ := json.Marshal(struct { - Name bool `json:"name"` - }{ - Name: true, - }) - handlerDetails = []HandlerDetails{ - {Method: http.MethodPost, Path: web.ServiceBrokersURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusCreated}, - } - }) - It("should return error", func() { - responseBroker, err := client.RegisterBroker(broker, params) - - Expect(err).Should(HaveOccurred()) - Expect(responseBroker).To(BeNil()) - }) - }) - - Context("When invalid status code is returned by SM", func() { - Context("And status code is unsuccessful", func() { - BeforeEach(func() { - responseBody, _ := json.Marshal(broker) - handlerDetails = []HandlerDetails{ - {Method: http.MethodPost, Path: web.ServiceBrokersURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, - } - }) - It("should return error with status code", func() { - responseBroker, err := client.RegisterBroker(broker, params) - - Expect(err).Should(HaveOccurred()) - verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) - Expect(responseBroker).To(BeNil()) - }) - }) - - Context("And status code is unsuccessful", func() { - BeforeEach(func() { - responseBody := []byte(`{ "description": "description", "error": "error"}`) - handlerDetails = []HandlerDetails{ - {Method: http.MethodPost, Path: web.ServiceBrokersURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusBadRequest}, - } - }) - It("should return error with url and description", func() { - responseBroker, err := client.RegisterBroker(broker, params) - - Expect(err).Should(HaveOccurred()) - verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) - Expect(responseBroker).To(BeNil()) - }) - }) - - Context("And invalid response body", func() { - BeforeEach(func() { - responseBody := []byte(`{ "description": description", "error": "error"}`) - handlerDetails = []HandlerDetails{ - {Method: http.MethodPost, Path: web.ServiceBrokersURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusBadRequest}, - } - }) - It("should return error without url and description if invalid response body", func() { - responseBroker, err := client.RegisterBroker(broker, params) - - Expect(err).Should(HaveOccurred()) - verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) - Expect(responseBroker).To(BeNil()) - }) - }) - - }) - - Context("When invalid config is set", func() { - It("should return error", func() { - client = NewClient(fakeAuthClient, "invalidURL") - _, err := client.RegisterBroker(broker, params) - - Expect(err).Should(HaveOccurred()) - }) - }) - }) - - Describe("List brokers", func() { - Context("when there are brokers registered", func() { - BeforeEach(func() { - brokersArray := []types.Broker{*broker} - brokers := types.Brokers{Brokers: brokersArray} - responseBody, _ := json.Marshal(brokers) - handlerDetails = []HandlerDetails{ - {Method: http.MethodGet, Path: web.ServiceBrokersURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, - } - }) - It("should return all", func() { - result, err := client.ListBrokers(params) - Expect(err).ShouldNot(HaveOccurred()) - Expect(result.Brokers).To(HaveLen(1)) - Expect(result.Brokers[0]).To(Equal(*broker)) - }) - }) - - Context("when there are no brokers registered", func() { - BeforeEach(func() { - brokersArray := []types.Broker{} - brokers := types.Brokers{Brokers: brokersArray} - responseBody, _ := json.Marshal(brokers) - handlerDetails = []HandlerDetails{ - {Method: http.MethodGet, Path: web.ServiceBrokersURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, - } - }) - It("should return an empty array", func() { - result, err := client.ListBrokers(params) - Expect(err).ShouldNot(HaveOccurred()) - Expect(result.Brokers).To(HaveLen(0)) - }) - }) - - Context("when invalid status code is returned", func() { - BeforeEach(func() { - handlerDetails = []HandlerDetails{ - {Method: http.MethodGet, Path: web.ServiceBrokersURL, ResponseStatusCode: http.StatusCreated}, - } - }) - It("should handle status code != 200", func() { - _, err := client.ListBrokers(params) - Expect(err).Should(HaveOccurred()) - verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) - }) - }) - - Context("when invalid status code is returned", func() { - BeforeEach(func() { - handlerDetails = []HandlerDetails{ - {Method: http.MethodGet, Path: web.ServiceBrokersURL, ResponseStatusCode: http.StatusBadRequest}, - } - }) - It("should handle status code > 299", func() { - _, err := client.ListBrokers(params) - Expect(err).Should(HaveOccurred()) - verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) - - }) - }) - }) - - Describe("List platforms", func() { - Context("when there are platforms registered", func() { - BeforeEach(func() { - platformsArray := []types.Platform{*platform} - platforms := types.Platforms{Platforms: platformsArray} - responseBody, _ := json.Marshal(platforms) - - handlerDetails = []HandlerDetails{ - {Method: http.MethodGet, Path: web.PlatformsURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, - } - }) - It("should return all", func() { - result, err := client.ListPlatforms(params) - Expect(err).ShouldNot(HaveOccurred()) - Expect(result.Platforms).To(HaveLen(1)) - Expect(result.Platforms[0]).To(Equal(*platform)) - }) - }) - - Context("when there are no platforms registered", func() { - BeforeEach(func() { - platformsArray := []types.Platform{} - platforms := types.Platforms{Platforms: platformsArray} - responseBody, _ := json.Marshal(platforms) - - handlerDetails = []HandlerDetails{ - {Method: http.MethodGet, Path: web.PlatformsURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, - } - }) - It("should return an empty array", func() { - result, err := client.ListPlatforms(params) - Expect(err).ShouldNot(HaveOccurred()) - Expect(result.Platforms).To(HaveLen(0)) - }) - }) - - Context("when invalid status code is returned", func() { - BeforeEach(func() { - handlerDetails = []HandlerDetails{ - {Method: http.MethodGet, Path: web.PlatformsURL, ResponseStatusCode: http.StatusCreated}, - } - }) - It("should handle status code != 200", func() { - _, err := client.ListPlatforms(params) - Expect(err).Should(HaveOccurred()) - verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) - }) - }) - Context("when invalid status code is returned", func() { - BeforeEach(func() { - handlerDetails = []HandlerDetails{ - {Method: http.MethodGet, Path: web.PlatformsURL, ResponseStatusCode: http.StatusBadRequest}, - } - }) - It("should handle status code > 299", func() { - _, err := client.ListPlatforms(params) - Expect(err).Should(HaveOccurred()) - verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) - }) - }) - }) - - Describe("List Visibilities", func() { - Context("when there are visibilities registered", func() { - BeforeEach(func() { - visibilitiesArray := []types.Visibility{*visibility} - visibilities := types.Visibilities{Visibilities: visibilitiesArray} - responseBody, _ := json.Marshal(visibilities) - - handlerDetails = []HandlerDetails{ - {Method: http.MethodGet, Path: web.VisibilitiesURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, - } - }) - It("should return all", func() { - result, err := client.ListVisibilities(params) - Expect(err).ShouldNot(HaveOccurred()) - Expect(result.Visibilities).To(HaveLen(1)) - Expect(result.Visibilities[0]).To(Equal(*visibility)) - }) - }) - - Context("when there are no visibilities registered", func() { - BeforeEach(func() { - visibilitiesArray := []types.Visibility{} - visibilities := types.Visibilities{Visibilities: visibilitiesArray} - responseBody, _ := json.Marshal(visibilities) - - handlerDetails = []HandlerDetails{ - {Method: http.MethodGet, Path: web.VisibilitiesURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, - } - }) - It("should return an empty array", func() { - result, err := client.ListVisibilities(params) - Expect(err).ShouldNot(HaveOccurred()) - Expect(result.Visibilities).To(HaveLen(0)) - }) - }) - - Context("when invalid status code is returned", func() { - BeforeEach(func() { - handlerDetails = []HandlerDetails{ - {Method: http.MethodGet, Path: web.VisibilitiesURL, ResponseStatusCode: http.StatusCreated}, - } - }) - It("should handle status code != 200", func() { - _, err := client.ListVisibilities(params) - Expect(err).Should(HaveOccurred()) - verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) - }) - }) - - Context("when invalid status code is returned", func() { - BeforeEach(func() { - handlerDetails = []HandlerDetails{ - {Method: http.MethodGet, Path: web.VisibilitiesURL, ResponseStatusCode: http.StatusBadRequest}, - } - }) - It("should handle status code > 299", func() { - _, err := client.ListVisibilities(params) - Expect(err).Should(HaveOccurred()) - verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) - }) - }) - }) - - Describe("List service instances", func() { - Context("when there are service instances registered", func() { - BeforeEach(func() { - instancesArray := []types.ServiceInstance{*instance} - instances := types.ServiceInstances{ServiceInstances: instancesArray} - responseBody, _ := json.Marshal(instances) - - handlerDetails = []HandlerDetails{ - {Method: http.MethodGet, Path: web.ServiceInstancesURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, - } - }) - It("should return all", func() { - result, err := client.ListInstances(params) - Expect(err).ShouldNot(HaveOccurred()) - Expect(result.ServiceInstances).To(HaveLen(1)) - Expect(result.ServiceInstances[0]).To(Equal(*instance)) - }) - }) - - Context("when there are no service instances registered", func() { - BeforeEach(func() { - instancesArray := []types.ServiceInstance{} - instances := types.ServiceInstances{ServiceInstances: instancesArray} - responseBody, _ := json.Marshal(instances) - - handlerDetails = []HandlerDetails{ - {Method: http.MethodGet, Path: web.ServiceInstancesURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, - } - }) - It("should return an empty array", func() { - result, err := client.ListInstances(params) - Expect(err).ShouldNot(HaveOccurred()) - Expect(result.ServiceInstances).To(HaveLen(0)) - }) - }) - - Context("when invalid status code is returned", func() { - BeforeEach(func() { - handlerDetails = []HandlerDetails{ - {Method: http.MethodGet, Path: web.ServiceInstancesURL, ResponseStatusCode: http.StatusCreated}, - } - }) - It("should handle status code != 200", func() { - _, err := client.ListInstances(params) - Expect(err).Should(HaveOccurred()) - verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) - }) - }) - - Context("when invalid status code is returned", func() { - BeforeEach(func() { - handlerDetails = []HandlerDetails{ - {Method: http.MethodGet, Path: web.ServiceInstancesURL, ResponseStatusCode: http.StatusBadRequest}, - } - }) - It("should handle status code > 299", func() { - _, err := client.ListInstances(params) - Expect(err).Should(HaveOccurred()) - verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) - }) - }) - }) - - Describe("List service bindings", func() { - Context("when there are service bindings registered", func() { - BeforeEach(func() { - bindingsArray := []types.ServiceBinding{*binding} - bindings := types.ServiceBindings{ServiceBindings: bindingsArray} - responseBody, _ := json.Marshal(bindings) - - handlerDetails = []HandlerDetails{ - {Method: http.MethodGet, Path: web.ServiceBindingsURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, - } - }) - It("should return all", func() { - result, err := client.ListBindings(params) - Expect(err).ShouldNot(HaveOccurred()) - Expect(result.ServiceBindings).To(HaveLen(1)) - Expect(result.ServiceBindings[0]).To(Equal(*binding)) - }) - }) - - Context("when there are no service bindings registered", func() { - BeforeEach(func() { - bindingsArray := []types.ServiceBinding{} - bindings := types.ServiceBindings{ServiceBindings: bindingsArray} - responseBody, _ := json.Marshal(bindings) - - handlerDetails = []HandlerDetails{ - {Method: http.MethodGet, Path: web.ServiceBindingsURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, - } - }) - It("should return an empty array", func() { - result, err := client.ListBindings(params) - Expect(err).ShouldNot(HaveOccurred()) - Expect(result.ServiceBindings).To(HaveLen(0)) - }) - }) - - Context("when invalid status code is returned", func() { - BeforeEach(func() { - handlerDetails = []HandlerDetails{ - {Method: http.MethodGet, Path: web.ServiceBindingsURL, ResponseStatusCode: http.StatusCreated}, - } - }) - It("should handle status code != 200", func() { - _, err := client.ListBindings(params) - Expect(err).Should(HaveOccurred()) - verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) - }) - }) - - Context("when invalid status code is returned", func() { - BeforeEach(func() { - handlerDetails = []HandlerDetails{ - {Method: http.MethodGet, Path: web.ServiceBindingsURL, ResponseStatusCode: http.StatusBadRequest}, - } - }) - It("should handle status code > 299", func() { - _, err := client.ListBindings(params) - Expect(err).Should(HaveOccurred()) - verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) - }) - }) - }) - - Describe("Marketplace", func() { - Context("when there are offerings provided", func() { - BeforeEach(func() { - offerings := types.ServiceOfferings{ServiceOfferings: []types.ServiceOffering{*initialOffering}} - offeringResponseBody, _ := json.Marshal(offerings) - - plans := types.ServicePlans{ServicePlans: []types.ServicePlan{*plan}} - plansResponseBody, _ := json.Marshal(plans) - - brokerResponseBody, _ := json.Marshal(broker) - - handlerDetails = []HandlerDetails{ - {Method: http.MethodGet, Path: web.ServiceOfferingsURL, ResponseBody: offeringResponseBody, ResponseStatusCode: http.StatusOK}, - {Method: http.MethodGet, Path: web.ServicePlansURL, ResponseBody: plansResponseBody, ResponseStatusCode: http.StatusOK}, - {Method: http.MethodGet, Path: web.ServiceBrokersURL + "/", ResponseBody: brokerResponseBody, ResponseStatusCode: http.StatusOK}, - } - }) - It("should return all with plans and broker name populated", func() { - result, err := client.Marketplace(params) - Expect(err).ShouldNot(HaveOccurred()) - Expect(result.ServiceOfferings).To(HaveLen(1)) - Expect(result.ServiceOfferings[0]).To(Equal(*resultOffering)) - }) - }) - - Context("when there are no offerings provided", func() { - BeforeEach(func() { - offerings := types.ServiceOfferings{} - offeringResponseBody, _ := json.Marshal(offerings) - - handlerDetails = []HandlerDetails{ - {Method: http.MethodGet, Path: web.ServiceOfferingsURL, ResponseBody: offeringResponseBody, ResponseStatusCode: http.StatusOK}, - } - }) - It("should return an empty array", func() { - result, err := client.Marketplace(params) - Expect(err).ShouldNot(HaveOccurred()) - Expect(result.ServiceOfferings).To(HaveLen(0)) - }) - }) - - Context("when invalid status code is returned", func() { - BeforeEach(func() { - handlerDetails = []HandlerDetails{ - {Method: http.MethodGet, Path: web.ServiceOfferingsURL, ResponseStatusCode: http.StatusCreated}, - } - }) - It("should handle status code != 200", func() { - _, err := client.Marketplace(params) - Expect(err).Should(HaveOccurred()) - verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) - }) - }) - - Context("when invalid status code is returned", func() { - BeforeEach(func() { - handlerDetails = []HandlerDetails{ - {Method: http.MethodGet, Path: web.ServiceOfferingsURL, ResponseStatusCode: http.StatusBadRequest}, - } - }) - It("should handle status code > 299", func() { - _, err := client.Marketplace(params) - Expect(err).Should(HaveOccurred()) - verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) - }) - }) - - }) - - Describe("Delete brokers", func() { - Context("when an existing broker is being deleted", func() { - BeforeEach(func() { - responseBody := []byte("{}") - - handlerDetails = []HandlerDetails{ - {Method: http.MethodDelete, Path: web.ServiceBrokersURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, - } - }) - It("should be successfully removed", func() { - params.FieldQuery = append(params.FieldQuery, "id eq 'id'") - err := client.DeleteBrokers(params) - Expect(err).ShouldNot(HaveOccurred()) - }) - }) - - Context("when service manager returns a non-expected status code", func() { - BeforeEach(func() { - responseBody := []byte("{}") - - handlerDetails = []HandlerDetails{ - {Method: http.MethodDelete, Path: web.ServiceBrokersURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusCreated}, - } - }) - It("should handle error", func() { - params.FieldQuery = append(params.FieldQuery, "id eq 'id'") - err := client.DeleteBrokers(params) - Expect(err).Should(HaveOccurred()) - verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) - }) - }) - - Context("when service manager returns a status code not found", func() { - BeforeEach(func() { - responseBody := []byte(`{ "description": "Broker not found" }`) - - handlerDetails = []HandlerDetails{ - {Method: http.MethodDelete, Path: web.ServiceBrokersURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusNotFound}, - } - }) - It("should handle error", func() { - params.FieldQuery = append(params.FieldQuery, "id eq 'id'") - err := client.DeleteBrokers(params) - Expect(err).Should(HaveOccurred()) - verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) - }) - }) - }) - - Describe("Delete platforms", func() { - Context("when an existing platform is being deleted", func() { - BeforeEach(func() { - responseBody := []byte("{}") - - handlerDetails = []HandlerDetails{ - {Method: http.MethodDelete, Path: web.PlatformsURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, - } - }) - It("should be successfully removed", func() { - params.FieldQuery = append(params.FieldQuery, "id eq 'id'") - err := client.DeletePlatforms(params) - Expect(err).ShouldNot(HaveOccurred()) - }) - }) - - Context("when service manager returns a non-expected status code", func() { - BeforeEach(func() { - responseBody := []byte("{}") - - handlerDetails = []HandlerDetails{ - {Method: http.MethodDelete, Path: web.PlatformsURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusCreated}, - } - }) - It("should handle error", func() { - params.FieldQuery = append(params.FieldQuery, "id eq 'id'") - err := client.DeletePlatforms(params) - Expect(err).Should(HaveOccurred()) - verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) - }) - }) - - Context("when service manager returns a status code not found", func() { - BeforeEach(func() { - responseBody := []byte(`{ "description": "Platform not found" }`) - - handlerDetails = []HandlerDetails{ - {Method: http.MethodDelete, Path: web.PlatformsURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusNotFound}, - } - }) - It("should handle error", func() { - params.FieldQuery = append(params.FieldQuery, "id eq 'id'") - err := client.DeletePlatforms(params) - Expect(err).Should(HaveOccurred()) - verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) - }) - }) - }) - - Describe("Delete visibility", func() { - Context("when an existing visibility is being deleted", func() { - BeforeEach(func() { - responseBody := []byte("{}") - - handlerDetails = []HandlerDetails{ - {Method: http.MethodDelete, Path: web.VisibilitiesURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, - } - }) - It("should be successfully removed", func() { - params.FieldQuery = append(params.FieldQuery, "id eq 'id'") - err := client.DeleteVisibilities(params) - Expect(err).ShouldNot(HaveOccurred()) - }) - }) - - Context("when service manager returns a non-expected status code", func() { - BeforeEach(func() { - responseBody := []byte("{}") - - handlerDetails = []HandlerDetails{ - {Method: http.MethodDelete, Path: web.VisibilitiesURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusCreated}, - } - }) - It("should handle error", func() { - params.FieldQuery = append(params.FieldQuery, "id eq 'id'") - err := client.DeleteVisibilities(params) - Expect(err).Should(HaveOccurred()) - verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) - }) - }) - - Context("when service manager returns a status code not found", func() { - BeforeEach(func() { - responseBody := []byte(`{ "description": "Visibility not found" }`) - - handlerDetails = []HandlerDetails{ - {Method: http.MethodDelete, Path: web.VisibilitiesURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusNotFound}, - } - }) - It("should handle error", func() { - params.FieldQuery = append(params.FieldQuery, "id eq 'id'") - err := client.DeleteVisibilities(params) - Expect(err).Should(HaveOccurred()) - verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) - }) - }) - }) - - Describe("Update brokers", func() { - Context("when an existing broker is being updated", func() { - BeforeEach(func() { - responseBody, _ := json.Marshal(broker) - - handlerDetails = []HandlerDetails{ - {Method: http.MethodPatch, Path: web.ServiceBrokersURL + "/", ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, - } - }) - It("should be successfully removed", func() { - updatedBroker, err := client.UpdateBroker("id", broker, params) - Expect(err).ShouldNot(HaveOccurred()) - Expect(updatedBroker).To(Equal(broker)) - }) - }) - - Context("when a non-existing broker is being updated", func() { - BeforeEach(func() { - responseBody := []byte(`{"description": "Broker not found"}`) - - handlerDetails = []HandlerDetails{ - {Method: http.MethodPatch, Path: web.ServiceBrokersURL + "/", ResponseBody: responseBody, ResponseStatusCode: http.StatusNotFound}, - } - }) - It("should handle error", func() { - _, err := client.UpdateBroker("id", broker, params) - Expect(err).Should(HaveOccurred()) - verifyErrorMsg(err.Error(), handlerDetails[0].Path+"id", handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) - }) - }) - - Context("when service manager returns a non-expected status code", func() { - BeforeEach(func() { - responseBody := []byte("{}") - - handlerDetails = []HandlerDetails{ - {Method: http.MethodPatch, Path: web.ServiceBrokersURL + "/", ResponseBody: responseBody, ResponseStatusCode: http.StatusCreated}, - } - }) - It("should handle error", func() { - _, err := client.UpdateBroker("id", broker, params) - Expect(err).Should(HaveOccurred()) - verifyErrorMsg(err.Error(), handlerDetails[0].Path+"id", handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) - }) - }) - }) - - Describe("Update platforms", func() { - Context("when an existing platform is being updated", func() { - BeforeEach(func() { - responseBody, _ := json.Marshal(platform) - - handlerDetails = []HandlerDetails{ - {Method: http.MethodPatch, Path: web.PlatformsURL + "/", ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, - } - }) - It("should be successfully removed", func() { - updatedPlatform, err := client.UpdatePlatform("id", platform, params) - Expect(err).ShouldNot(HaveOccurred()) - Expect(updatedPlatform).To(Equal(platform)) - }) - }) - - Context("when a non-existing platform is being updated", func() { - BeforeEach(func() { - responseBody := []byte(`{"description": "Platform not found"}`) - - handlerDetails = []HandlerDetails{ - {Method: http.MethodPatch, Path: web.PlatformsURL + "/", ResponseBody: responseBody, ResponseStatusCode: http.StatusNotFound}, - } - }) - It("should handle error", func() { - _, err := client.UpdatePlatform("id", platform, params) - Expect(err).Should(HaveOccurred()) - verifyErrorMsg(err.Error(), handlerDetails[0].Path+"id", handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) - }) - }) - - Context("when service manager returns a non-expected status code", func() { - BeforeEach(func() { - responseBody := []byte("{}") - - handlerDetails = []HandlerDetails{ - {Method: http.MethodPatch, Path: web.PlatformsURL + "/", ResponseBody: responseBody, ResponseStatusCode: http.StatusCreated}, - } - }) - It("should handle error", func() { - _, err := client.UpdatePlatform("id", platform, params) - Expect(err).Should(HaveOccurred()) - verifyErrorMsg(err.Error(), handlerDetails[0].Path+"id", handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) - }) - }) - }) - - Describe("Update visibilities", func() { - Context("when an existing visibility is being updated", func() { - BeforeEach(func() { - responseBody, _ := json.Marshal(visibility) - - handlerDetails = []HandlerDetails{ - {Method: http.MethodPatch, Path: web.VisibilitiesURL + "/", ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, - } - }) - It("should be successfully removed", func() { - updatedVisibility, err := client.UpdateVisibility("id", visibility, params) - Expect(err).ShouldNot(HaveOccurred()) - Expect(updatedVisibility).To(Equal(visibility)) - }) - }) - - Context("when a non-existing visibility is being updated", func() { - BeforeEach(func() { - responseBody := []byte(`{"description": "Visibility not found"}`) - - handlerDetails = []HandlerDetails{ - {Method: http.MethodPatch, Path: web.VisibilitiesURL + "/", ResponseBody: responseBody, ResponseStatusCode: http.StatusNotFound}, - } - }) - It("should handle error", func() { - _, err := client.UpdateVisibility("id", visibility, params) - Expect(err).Should(HaveOccurred()) - verifyErrorMsg(err.Error(), handlerDetails[0].Path+"id", handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) - }) - }) - - Context("when service manager returns a non-expected status code", func() { - BeforeEach(func() { - responseBody := []byte("{}") - - handlerDetails = []HandlerDetails{ - {Method: http.MethodPatch, Path: web.VisibilitiesURL + "/", ResponseBody: responseBody, ResponseStatusCode: http.StatusCreated}, - } - }) - It("should handle error", func() { - _, err := client.UpdateVisibility("id", visibility, params) - Expect(err).Should(HaveOccurred()) - verifyErrorMsg(err.Error(), handlerDetails[0].Path+"id", handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) - - }) - }) - }) - - Describe("Get info", func() { - Context("when token issuer is set", func() { - BeforeEach(func() { - responseBody := []byte(`{"token_issuer_url": "http://uaa.com"}`) - - handlerDetails = []HandlerDetails{ - {Method: http.MethodGet, Path: web.InfoURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, - } - }) - It("should get the right issuer and default token basic auth", func() { - info, _ := client.GetInfo(params) - Expect(info.TokenIssuerURL).To(Equal("http://uaa.com")) - Expect(info.TokenBasicAuth).To(BeTrue()) // default value - }) - }) - - Context("when token basic auth is set", func() { - BeforeEach(func() { - responseBody := []byte(`{"token_issuer_url": "http://uaa.com", "token_basic_auth": false}`) - - handlerDetails = []HandlerDetails{ - {Method: http.MethodGet, Path: web.InfoURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, - } - }) - It("should get the right value", func() { - info, _ := client.GetInfo(params) - Expect(info.TokenIssuerURL).To(Equal("http://uaa.com")) - Expect(info.TokenBasicAuth).To(BeFalse()) - }) - }) - - Context("when invalid status code is returned", func() { - BeforeEach(func() { - responseBody := []byte(``) - - handlerDetails = []HandlerDetails{ - {Method: http.MethodGet, Path: web.InfoURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusNotFound}, - } - }) - It("should get an error", func() { - _, err := client.GetInfo(params) - Expect(err).Should(HaveOccurred()) - verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) - - }) - }) - - Context("when invalid json is returned", func() { - BeforeEach(func() { - responseBody := []byte(`{"token_issuer":}`) - - handlerDetails = []HandlerDetails{ - {Method: http.MethodGet, Path: web.InfoURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, - } - }) - It("should get an error", func() { - _, err := client.GetInfo(params) - Expect(err).Should(HaveOccurred()) - }) - }) - }) - - Describe("Label", func() { - Context("when valid label change is sent", func() { - BeforeEach(func() { - handlerDetails = []HandlerDetails{{Method: http.MethodPatch, Path: web.ServiceBrokersURL + "/", ResponseStatusCode: http.StatusOK}} - }) - It("should label resource sucessfully", func() { - - err := client.Label(web.ServiceBrokersURL, "id", labelChanges, params) - Expect(err).ShouldNot(HaveOccurred()) - }) - }) - - Context("when invalid status code is returned", func() { - BeforeEach(func() { - handlerDetails = []HandlerDetails{{Method: http.MethodPatch, Path: web.ServiceBrokersURL + "/", ResponseStatusCode: http.StatusNotFound}} - }) - It("should return error", func() { - err := client.Label(web.ServiceBrokersURL, "id", labelChanges, params) - Expect(err).Should(HaveOccurred()) - verifyErrorMsg(err.Error(), handlerDetails[0].Path+"id", handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) - }) - }) - - Context("When invalid config is set", func() { - It("should return error", func() { - client = NewClient(fakeAuthClient, "invalidURL") - err := client.Label(web.ServiceBrokersURL, "id", labelChanges, params) - Expect(err).Should(HaveOccurred()) - }) - }) - }) -}) diff --git a/pkg/smclient/smclientfakes/fake_client.go b/pkg/smclient/smclientfakes/fake_client.go index db4ebad6..64b4c793 100644 --- a/pkg/smclient/smclientfakes/fake_client.go +++ b/pkg/smclient/smclientfakes/fake_client.go @@ -12,6 +12,22 @@ import ( ) type FakeClient struct { + BindStub func(*types.ServiceBinding, *query.Parameters) (*types.ServiceBinding, string, error) + bindMutex sync.RWMutex + bindArgsForCall []struct { + arg1 *types.ServiceBinding + arg2 *query.Parameters + } + bindReturns struct { + result1 *types.ServiceBinding + result2 string + result3 error + } + bindReturnsOnCall map[int]struct { + result1 *types.ServiceBinding + result2 string + result3 error + } CallStub func(string, string, io.Reader, *query.Parameters) (*http.Response, error) callMutex sync.RWMutex callArgsForCall []struct { @@ -28,16 +44,19 @@ type FakeClient struct { result1 *http.Response result2 error } - DeleteBrokersStub func(*query.Parameters) error - deleteBrokersMutex sync.RWMutex - deleteBrokersArgsForCall []struct { - arg1 *query.Parameters + DeleteBrokerStub func(string, *query.Parameters) (string, error) + deleteBrokerMutex sync.RWMutex + deleteBrokerArgsForCall []struct { + arg1 string + arg2 *query.Parameters } - deleteBrokersReturns struct { - result1 error + deleteBrokerReturns struct { + result1 string + result2 error } - deleteBrokersReturnsOnCall map[int]struct { - result1 error + deleteBrokerReturnsOnCall map[int]struct { + result1 string + result2 error } DeletePlatformsStub func(*query.Parameters) error deletePlatformsMutex sync.RWMutex @@ -61,6 +80,20 @@ type FakeClient struct { deleteVisibilitiesReturnsOnCall map[int]struct { result1 error } + DeprovisionStub func(string, *query.Parameters) (string, error) + deprovisionMutex sync.RWMutex + deprovisionArgsForCall []struct { + arg1 string + arg2 *query.Parameters + } + deprovisionReturns struct { + result1 string + result2 error + } + deprovisionReturnsOnCall map[int]struct { + result1 string + result2 error + } GetBindingByIDStub func(string, *query.Parameters) (*types.ServiceBinding, error) getBindingByIDMutex sync.RWMutex getBindingByIDArgsForCall []struct { @@ -234,7 +267,23 @@ type FakeClient struct { result1 *types.Marketplace result2 error } - RegisterBrokerStub func(*types.Broker, *query.Parameters) (*types.Broker, error) + ProvisionStub func(*types.ServiceInstance, *query.Parameters) (*types.ServiceInstance, string, error) + provisionMutex sync.RWMutex + provisionArgsForCall []struct { + arg1 *types.ServiceInstance + arg2 *query.Parameters + } + provisionReturns struct { + result1 *types.ServiceInstance + result2 string + result3 error + } + provisionReturnsOnCall map[int]struct { + result1 *types.ServiceInstance + result2 string + result3 error + } + RegisterBrokerStub func(*types.Broker, *query.Parameters) (*types.Broker, string, error) registerBrokerMutex sync.RWMutex registerBrokerArgsForCall []struct { arg1 *types.Broker @@ -242,11 +291,13 @@ type FakeClient struct { } registerBrokerReturns struct { result1 *types.Broker - result2 error + result2 string + result3 error } registerBrokerReturnsOnCall map[int]struct { result1 *types.Broker - result2 error + result2 string + result3 error } RegisterPlatformStub func(*types.Platform, *query.Parameters) (*types.Platform, error) registerPlatformMutex sync.RWMutex @@ -276,7 +327,35 @@ type FakeClient struct { result1 *types.Visibility result2 error } - UpdateBrokerStub func(string, *types.Broker, *query.Parameters) (*types.Broker, error) + StatusStub func(string, *query.Parameters) (*types.Operation, error) + statusMutex sync.RWMutex + statusArgsForCall []struct { + arg1 string + arg2 *query.Parameters + } + statusReturns struct { + result1 *types.Operation + result2 error + } + statusReturnsOnCall map[int]struct { + result1 *types.Operation + result2 error + } + UnbindStub func(string, *query.Parameters) (string, error) + unbindMutex sync.RWMutex + unbindArgsForCall []struct { + arg1 string + arg2 *query.Parameters + } + unbindReturns struct { + result1 string + result2 error + } + unbindReturnsOnCall map[int]struct { + result1 string + result2 error + } + UpdateBrokerStub func(string, *types.Broker, *query.Parameters) (*types.Broker, string, error) updateBrokerMutex sync.RWMutex updateBrokerArgsForCall []struct { arg1 string @@ -285,11 +364,13 @@ type FakeClient struct { } updateBrokerReturns struct { result1 *types.Broker - result2 error + result2 string + result3 error } updateBrokerReturnsOnCall map[int]struct { result1 *types.Broker - result2 error + result2 string + result3 error } UpdatePlatformStub func(string, *types.Platform, *query.Parameters) (*types.Platform, error) updatePlatformMutex sync.RWMutex @@ -325,6 +406,73 @@ type FakeClient struct { invocationsMutex sync.RWMutex } +func (fake *FakeClient) Bind(arg1 *types.ServiceBinding, arg2 *query.Parameters) (*types.ServiceBinding, string, error) { + fake.bindMutex.Lock() + ret, specificReturn := fake.bindReturnsOnCall[len(fake.bindArgsForCall)] + fake.bindArgsForCall = append(fake.bindArgsForCall, struct { + arg1 *types.ServiceBinding + arg2 *query.Parameters + }{arg1, arg2}) + fake.recordInvocation("Bind", []interface{}{arg1, arg2}) + fake.bindMutex.Unlock() + if fake.BindStub != nil { + return fake.BindStub(arg1, arg2) + } + if specificReturn { + return ret.result1, ret.result2, ret.result3 + } + fakeReturns := fake.bindReturns + return fakeReturns.result1, fakeReturns.result2, fakeReturns.result3 +} + +func (fake *FakeClient) BindCallCount() int { + fake.bindMutex.RLock() + defer fake.bindMutex.RUnlock() + return len(fake.bindArgsForCall) +} + +func (fake *FakeClient) BindCalls(stub func(*types.ServiceBinding, *query.Parameters) (*types.ServiceBinding, string, error)) { + fake.bindMutex.Lock() + defer fake.bindMutex.Unlock() + fake.BindStub = stub +} + +func (fake *FakeClient) BindArgsForCall(i int) (*types.ServiceBinding, *query.Parameters) { + fake.bindMutex.RLock() + defer fake.bindMutex.RUnlock() + argsForCall := fake.bindArgsForCall[i] + return argsForCall.arg1, argsForCall.arg2 +} + +func (fake *FakeClient) BindReturns(result1 *types.ServiceBinding, result2 string, result3 error) { + fake.bindMutex.Lock() + defer fake.bindMutex.Unlock() + fake.BindStub = nil + fake.bindReturns = struct { + result1 *types.ServiceBinding + result2 string + result3 error + }{result1, result2, result3} +} + +func (fake *FakeClient) BindReturnsOnCall(i int, result1 *types.ServiceBinding, result2 string, result3 error) { + fake.bindMutex.Lock() + defer fake.bindMutex.Unlock() + fake.BindStub = nil + if fake.bindReturnsOnCall == nil { + fake.bindReturnsOnCall = make(map[int]struct { + result1 *types.ServiceBinding + result2 string + result3 error + }) + } + fake.bindReturnsOnCall[i] = struct { + result1 *types.ServiceBinding + result2 string + result3 error + }{result1, result2, result3} +} + func (fake *FakeClient) Call(arg1 string, arg2 string, arg3 io.Reader, arg4 *query.Parameters) (*http.Response, error) { fake.callMutex.Lock() ret, specificReturn := fake.callReturnsOnCall[len(fake.callArgsForCall)] @@ -391,64 +539,68 @@ func (fake *FakeClient) CallReturnsOnCall(i int, result1 *http.Response, result2 }{result1, result2} } -func (fake *FakeClient) DeleteBrokers(arg1 *query.Parameters) error { - fake.deleteBrokersMutex.Lock() - ret, specificReturn := fake.deleteBrokersReturnsOnCall[len(fake.deleteBrokersArgsForCall)] - fake.deleteBrokersArgsForCall = append(fake.deleteBrokersArgsForCall, struct { - arg1 *query.Parameters - }{arg1}) - fake.recordInvocation("DeleteBrokers", []interface{}{arg1}) - fake.deleteBrokersMutex.Unlock() - if fake.DeleteBrokersStub != nil { - return fake.DeleteBrokersStub(arg1) +func (fake *FakeClient) DeleteBroker(arg1 string, arg2 *query.Parameters) (string, error) { + fake.deleteBrokerMutex.Lock() + ret, specificReturn := fake.deleteBrokerReturnsOnCall[len(fake.deleteBrokerArgsForCall)] + fake.deleteBrokerArgsForCall = append(fake.deleteBrokerArgsForCall, struct { + arg1 string + arg2 *query.Parameters + }{arg1, arg2}) + fake.recordInvocation("DeleteBroker", []interface{}{arg1, arg2}) + fake.deleteBrokerMutex.Unlock() + if fake.DeleteBrokerStub != nil { + return fake.DeleteBrokerStub(arg1, arg2) } if specificReturn { - return ret.result1 + return ret.result1, ret.result2 } - fakeReturns := fake.deleteBrokersReturns - return fakeReturns.result1 + fakeReturns := fake.deleteBrokerReturns + return fakeReturns.result1, fakeReturns.result2 } -func (fake *FakeClient) DeleteBrokersCallCount() int { - fake.deleteBrokersMutex.RLock() - defer fake.deleteBrokersMutex.RUnlock() - return len(fake.deleteBrokersArgsForCall) +func (fake *FakeClient) DeleteBrokerCallCount() int { + fake.deleteBrokerMutex.RLock() + defer fake.deleteBrokerMutex.RUnlock() + return len(fake.deleteBrokerArgsForCall) } -func (fake *FakeClient) DeleteBrokersCalls(stub func(*query.Parameters) error) { - fake.deleteBrokersMutex.Lock() - defer fake.deleteBrokersMutex.Unlock() - fake.DeleteBrokersStub = stub +func (fake *FakeClient) DeleteBrokerCalls(stub func(string, *query.Parameters) (string, error)) { + fake.deleteBrokerMutex.Lock() + defer fake.deleteBrokerMutex.Unlock() + fake.DeleteBrokerStub = stub } -func (fake *FakeClient) DeleteBrokersArgsForCall(i int) *query.Parameters { - fake.deleteBrokersMutex.RLock() - defer fake.deleteBrokersMutex.RUnlock() - argsForCall := fake.deleteBrokersArgsForCall[i] - return argsForCall.arg1 +func (fake *FakeClient) DeleteBrokerArgsForCall(i int) (string, *query.Parameters) { + fake.deleteBrokerMutex.RLock() + defer fake.deleteBrokerMutex.RUnlock() + argsForCall := fake.deleteBrokerArgsForCall[i] + return argsForCall.arg1, argsForCall.arg2 } -func (fake *FakeClient) DeleteBrokersReturns(result1 error) { - fake.deleteBrokersMutex.Lock() - defer fake.deleteBrokersMutex.Unlock() - fake.DeleteBrokersStub = nil - fake.deleteBrokersReturns = struct { - result1 error - }{result1} +func (fake *FakeClient) DeleteBrokerReturns(result1 string, result2 error) { + fake.deleteBrokerMutex.Lock() + defer fake.deleteBrokerMutex.Unlock() + fake.DeleteBrokerStub = nil + fake.deleteBrokerReturns = struct { + result1 string + result2 error + }{result1, result2} } -func (fake *FakeClient) DeleteBrokersReturnsOnCall(i int, result1 error) { - fake.deleteBrokersMutex.Lock() - defer fake.deleteBrokersMutex.Unlock() - fake.DeleteBrokersStub = nil - if fake.deleteBrokersReturnsOnCall == nil { - fake.deleteBrokersReturnsOnCall = make(map[int]struct { - result1 error +func (fake *FakeClient) DeleteBrokerReturnsOnCall(i int, result1 string, result2 error) { + fake.deleteBrokerMutex.Lock() + defer fake.deleteBrokerMutex.Unlock() + fake.DeleteBrokerStub = nil + if fake.deleteBrokerReturnsOnCall == nil { + fake.deleteBrokerReturnsOnCall = make(map[int]struct { + result1 string + result2 error }) } - fake.deleteBrokersReturnsOnCall[i] = struct { - result1 error - }{result1} + fake.deleteBrokerReturnsOnCall[i] = struct { + result1 string + result2 error + }{result1, result2} } func (fake *FakeClient) DeletePlatforms(arg1 *query.Parameters) error { @@ -571,6 +723,70 @@ func (fake *FakeClient) DeleteVisibilitiesReturnsOnCall(i int, result1 error) { }{result1} } +func (fake *FakeClient) Deprovision(arg1 string, arg2 *query.Parameters) (string, error) { + fake.deprovisionMutex.Lock() + ret, specificReturn := fake.deprovisionReturnsOnCall[len(fake.deprovisionArgsForCall)] + fake.deprovisionArgsForCall = append(fake.deprovisionArgsForCall, struct { + arg1 string + arg2 *query.Parameters + }{arg1, arg2}) + fake.recordInvocation("Deprovision", []interface{}{arg1, arg2}) + fake.deprovisionMutex.Unlock() + if fake.DeprovisionStub != nil { + return fake.DeprovisionStub(arg1, arg2) + } + if specificReturn { + return ret.result1, ret.result2 + } + fakeReturns := fake.deprovisionReturns + return fakeReturns.result1, fakeReturns.result2 +} + +func (fake *FakeClient) DeprovisionCallCount() int { + fake.deprovisionMutex.RLock() + defer fake.deprovisionMutex.RUnlock() + return len(fake.deprovisionArgsForCall) +} + +func (fake *FakeClient) DeprovisionCalls(stub func(string, *query.Parameters) (string, error)) { + fake.deprovisionMutex.Lock() + defer fake.deprovisionMutex.Unlock() + fake.DeprovisionStub = stub +} + +func (fake *FakeClient) DeprovisionArgsForCall(i int) (string, *query.Parameters) { + fake.deprovisionMutex.RLock() + defer fake.deprovisionMutex.RUnlock() + argsForCall := fake.deprovisionArgsForCall[i] + return argsForCall.arg1, argsForCall.arg2 +} + +func (fake *FakeClient) DeprovisionReturns(result1 string, result2 error) { + fake.deprovisionMutex.Lock() + defer fake.deprovisionMutex.Unlock() + fake.DeprovisionStub = nil + fake.deprovisionReturns = struct { + result1 string + result2 error + }{result1, result2} +} + +func (fake *FakeClient) DeprovisionReturnsOnCall(i int, result1 string, result2 error) { + fake.deprovisionMutex.Lock() + defer fake.deprovisionMutex.Unlock() + fake.DeprovisionStub = nil + if fake.deprovisionReturnsOnCall == nil { + fake.deprovisionReturnsOnCall = make(map[int]struct { + result1 string + result2 error + }) + } + fake.deprovisionReturnsOnCall[i] = struct { + result1 string + result2 error + }{result1, result2} +} + func (fake *FakeClient) GetBindingByID(arg1 string, arg2 *query.Parameters) (*types.ServiceBinding, error) { fake.getBindingByIDMutex.Lock() ret, specificReturn := fake.getBindingByIDReturnsOnCall[len(fake.getBindingByIDArgsForCall)] @@ -1393,7 +1609,74 @@ func (fake *FakeClient) MarketplaceReturnsOnCall(i int, result1 *types.Marketpla }{result1, result2} } -func (fake *FakeClient) RegisterBroker(arg1 *types.Broker, arg2 *query.Parameters) (*types.Broker, error) { +func (fake *FakeClient) Provision(arg1 *types.ServiceInstance, arg2 *query.Parameters) (*types.ServiceInstance, string, error) { + fake.provisionMutex.Lock() + ret, specificReturn := fake.provisionReturnsOnCall[len(fake.provisionArgsForCall)] + fake.provisionArgsForCall = append(fake.provisionArgsForCall, struct { + arg1 *types.ServiceInstance + arg2 *query.Parameters + }{arg1, arg2}) + fake.recordInvocation("Provision", []interface{}{arg1, arg2}) + fake.provisionMutex.Unlock() + if fake.ProvisionStub != nil { + return fake.ProvisionStub(arg1, arg2) + } + if specificReturn { + return ret.result1, ret.result2, ret.result3 + } + fakeReturns := fake.provisionReturns + return fakeReturns.result1, fakeReturns.result2, fakeReturns.result3 +} + +func (fake *FakeClient) ProvisionCallCount() int { + fake.provisionMutex.RLock() + defer fake.provisionMutex.RUnlock() + return len(fake.provisionArgsForCall) +} + +func (fake *FakeClient) ProvisionCalls(stub func(*types.ServiceInstance, *query.Parameters) (*types.ServiceInstance, string, error)) { + fake.provisionMutex.Lock() + defer fake.provisionMutex.Unlock() + fake.ProvisionStub = stub +} + +func (fake *FakeClient) ProvisionArgsForCall(i int) (*types.ServiceInstance, *query.Parameters) { + fake.provisionMutex.RLock() + defer fake.provisionMutex.RUnlock() + argsForCall := fake.provisionArgsForCall[i] + return argsForCall.arg1, argsForCall.arg2 +} + +func (fake *FakeClient) ProvisionReturns(result1 *types.ServiceInstance, result2 string, result3 error) { + fake.provisionMutex.Lock() + defer fake.provisionMutex.Unlock() + fake.ProvisionStub = nil + fake.provisionReturns = struct { + result1 *types.ServiceInstance + result2 string + result3 error + }{result1, result2, result3} +} + +func (fake *FakeClient) ProvisionReturnsOnCall(i int, result1 *types.ServiceInstance, result2 string, result3 error) { + fake.provisionMutex.Lock() + defer fake.provisionMutex.Unlock() + fake.ProvisionStub = nil + if fake.provisionReturnsOnCall == nil { + fake.provisionReturnsOnCall = make(map[int]struct { + result1 *types.ServiceInstance + result2 string + result3 error + }) + } + fake.provisionReturnsOnCall[i] = struct { + result1 *types.ServiceInstance + result2 string + result3 error + }{result1, result2, result3} +} + +func (fake *FakeClient) RegisterBroker(arg1 *types.Broker, arg2 *query.Parameters) (*types.Broker, string, error) { fake.registerBrokerMutex.Lock() ret, specificReturn := fake.registerBrokerReturnsOnCall[len(fake.registerBrokerArgsForCall)] fake.registerBrokerArgsForCall = append(fake.registerBrokerArgsForCall, struct { @@ -1406,10 +1689,10 @@ func (fake *FakeClient) RegisterBroker(arg1 *types.Broker, arg2 *query.Parameter return fake.RegisterBrokerStub(arg1, arg2) } if specificReturn { - return ret.result1, ret.result2 + return ret.result1, ret.result2, ret.result3 } fakeReturns := fake.registerBrokerReturns - return fakeReturns.result1, fakeReturns.result2 + return fakeReturns.result1, fakeReturns.result2, fakeReturns.result3 } func (fake *FakeClient) RegisterBrokerCallCount() int { @@ -1418,7 +1701,7 @@ func (fake *FakeClient) RegisterBrokerCallCount() int { return len(fake.registerBrokerArgsForCall) } -func (fake *FakeClient) RegisterBrokerCalls(stub func(*types.Broker, *query.Parameters) (*types.Broker, error)) { +func (fake *FakeClient) RegisterBrokerCalls(stub func(*types.Broker, *query.Parameters) (*types.Broker, string, error)) { fake.registerBrokerMutex.Lock() defer fake.registerBrokerMutex.Unlock() fake.RegisterBrokerStub = stub @@ -1431,30 +1714,33 @@ func (fake *FakeClient) RegisterBrokerArgsForCall(i int) (*types.Broker, *query. return argsForCall.arg1, argsForCall.arg2 } -func (fake *FakeClient) RegisterBrokerReturns(result1 *types.Broker, result2 error) { +func (fake *FakeClient) RegisterBrokerReturns(result1 *types.Broker, result2 string, result3 error) { fake.registerBrokerMutex.Lock() defer fake.registerBrokerMutex.Unlock() fake.RegisterBrokerStub = nil fake.registerBrokerReturns = struct { result1 *types.Broker - result2 error - }{result1, result2} + result2 string + result3 error + }{result1, result2, result3} } -func (fake *FakeClient) RegisterBrokerReturnsOnCall(i int, result1 *types.Broker, result2 error) { +func (fake *FakeClient) RegisterBrokerReturnsOnCall(i int, result1 *types.Broker, result2 string, result3 error) { fake.registerBrokerMutex.Lock() defer fake.registerBrokerMutex.Unlock() fake.RegisterBrokerStub = nil if fake.registerBrokerReturnsOnCall == nil { fake.registerBrokerReturnsOnCall = make(map[int]struct { result1 *types.Broker - result2 error + result2 string + result3 error }) } fake.registerBrokerReturnsOnCall[i] = struct { result1 *types.Broker - result2 error - }{result1, result2} + result2 string + result3 error + }{result1, result2, result3} } func (fake *FakeClient) RegisterPlatform(arg1 *types.Platform, arg2 *query.Parameters) (*types.Platform, error) { @@ -1585,7 +1871,135 @@ func (fake *FakeClient) RegisterVisibilityReturnsOnCall(i int, result1 *types.Vi }{result1, result2} } -func (fake *FakeClient) UpdateBroker(arg1 string, arg2 *types.Broker, arg3 *query.Parameters) (*types.Broker, error) { +func (fake *FakeClient) Status(arg1 string, arg2 *query.Parameters) (*types.Operation, error) { + fake.statusMutex.Lock() + ret, specificReturn := fake.statusReturnsOnCall[len(fake.statusArgsForCall)] + fake.statusArgsForCall = append(fake.statusArgsForCall, struct { + arg1 string + arg2 *query.Parameters + }{arg1, arg2}) + fake.recordInvocation("Status", []interface{}{arg1, arg2}) + fake.statusMutex.Unlock() + if fake.StatusStub != nil { + return fake.StatusStub(arg1, arg2) + } + if specificReturn { + return ret.result1, ret.result2 + } + fakeReturns := fake.statusReturns + return fakeReturns.result1, fakeReturns.result2 +} + +func (fake *FakeClient) StatusCallCount() int { + fake.statusMutex.RLock() + defer fake.statusMutex.RUnlock() + return len(fake.statusArgsForCall) +} + +func (fake *FakeClient) StatusCalls(stub func(string, *query.Parameters) (*types.Operation, error)) { + fake.statusMutex.Lock() + defer fake.statusMutex.Unlock() + fake.StatusStub = stub +} + +func (fake *FakeClient) StatusArgsForCall(i int) (string, *query.Parameters) { + fake.statusMutex.RLock() + defer fake.statusMutex.RUnlock() + argsForCall := fake.statusArgsForCall[i] + return argsForCall.arg1, argsForCall.arg2 +} + +func (fake *FakeClient) StatusReturns(result1 *types.Operation, result2 error) { + fake.statusMutex.Lock() + defer fake.statusMutex.Unlock() + fake.StatusStub = nil + fake.statusReturns = struct { + result1 *types.Operation + result2 error + }{result1, result2} +} + +func (fake *FakeClient) StatusReturnsOnCall(i int, result1 *types.Operation, result2 error) { + fake.statusMutex.Lock() + defer fake.statusMutex.Unlock() + fake.StatusStub = nil + if fake.statusReturnsOnCall == nil { + fake.statusReturnsOnCall = make(map[int]struct { + result1 *types.Operation + result2 error + }) + } + fake.statusReturnsOnCall[i] = struct { + result1 *types.Operation + result2 error + }{result1, result2} +} + +func (fake *FakeClient) Unbind(arg1 string, arg2 *query.Parameters) (string, error) { + fake.unbindMutex.Lock() + ret, specificReturn := fake.unbindReturnsOnCall[len(fake.unbindArgsForCall)] + fake.unbindArgsForCall = append(fake.unbindArgsForCall, struct { + arg1 string + arg2 *query.Parameters + }{arg1, arg2}) + fake.recordInvocation("Unbind", []interface{}{arg1, arg2}) + fake.unbindMutex.Unlock() + if fake.UnbindStub != nil { + return fake.UnbindStub(arg1, arg2) + } + if specificReturn { + return ret.result1, ret.result2 + } + fakeReturns := fake.unbindReturns + return fakeReturns.result1, fakeReturns.result2 +} + +func (fake *FakeClient) UnbindCallCount() int { + fake.unbindMutex.RLock() + defer fake.unbindMutex.RUnlock() + return len(fake.unbindArgsForCall) +} + +func (fake *FakeClient) UnbindCalls(stub func(string, *query.Parameters) (string, error)) { + fake.unbindMutex.Lock() + defer fake.unbindMutex.Unlock() + fake.UnbindStub = stub +} + +func (fake *FakeClient) UnbindArgsForCall(i int) (string, *query.Parameters) { + fake.unbindMutex.RLock() + defer fake.unbindMutex.RUnlock() + argsForCall := fake.unbindArgsForCall[i] + return argsForCall.arg1, argsForCall.arg2 +} + +func (fake *FakeClient) UnbindReturns(result1 string, result2 error) { + fake.unbindMutex.Lock() + defer fake.unbindMutex.Unlock() + fake.UnbindStub = nil + fake.unbindReturns = struct { + result1 string + result2 error + }{result1, result2} +} + +func (fake *FakeClient) UnbindReturnsOnCall(i int, result1 string, result2 error) { + fake.unbindMutex.Lock() + defer fake.unbindMutex.Unlock() + fake.UnbindStub = nil + if fake.unbindReturnsOnCall == nil { + fake.unbindReturnsOnCall = make(map[int]struct { + result1 string + result2 error + }) + } + fake.unbindReturnsOnCall[i] = struct { + result1 string + result2 error + }{result1, result2} +} + +func (fake *FakeClient) UpdateBroker(arg1 string, arg2 *types.Broker, arg3 *query.Parameters) (*types.Broker, string, error) { fake.updateBrokerMutex.Lock() ret, specificReturn := fake.updateBrokerReturnsOnCall[len(fake.updateBrokerArgsForCall)] fake.updateBrokerArgsForCall = append(fake.updateBrokerArgsForCall, struct { @@ -1599,10 +2013,10 @@ func (fake *FakeClient) UpdateBroker(arg1 string, arg2 *types.Broker, arg3 *quer return fake.UpdateBrokerStub(arg1, arg2, arg3) } if specificReturn { - return ret.result1, ret.result2 + return ret.result1, ret.result2, ret.result3 } fakeReturns := fake.updateBrokerReturns - return fakeReturns.result1, fakeReturns.result2 + return fakeReturns.result1, fakeReturns.result2, fakeReturns.result3 } func (fake *FakeClient) UpdateBrokerCallCount() int { @@ -1611,7 +2025,7 @@ func (fake *FakeClient) UpdateBrokerCallCount() int { return len(fake.updateBrokerArgsForCall) } -func (fake *FakeClient) UpdateBrokerCalls(stub func(string, *types.Broker, *query.Parameters) (*types.Broker, error)) { +func (fake *FakeClient) UpdateBrokerCalls(stub func(string, *types.Broker, *query.Parameters) (*types.Broker, string, error)) { fake.updateBrokerMutex.Lock() defer fake.updateBrokerMutex.Unlock() fake.UpdateBrokerStub = stub @@ -1624,30 +2038,33 @@ func (fake *FakeClient) UpdateBrokerArgsForCall(i int) (string, *types.Broker, * return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3 } -func (fake *FakeClient) UpdateBrokerReturns(result1 *types.Broker, result2 error) { +func (fake *FakeClient) UpdateBrokerReturns(result1 *types.Broker, result2 string, result3 error) { fake.updateBrokerMutex.Lock() defer fake.updateBrokerMutex.Unlock() fake.UpdateBrokerStub = nil fake.updateBrokerReturns = struct { result1 *types.Broker - result2 error - }{result1, result2} + result2 string + result3 error + }{result1, result2, result3} } -func (fake *FakeClient) UpdateBrokerReturnsOnCall(i int, result1 *types.Broker, result2 error) { +func (fake *FakeClient) UpdateBrokerReturnsOnCall(i int, result1 *types.Broker, result2 string, result3 error) { fake.updateBrokerMutex.Lock() defer fake.updateBrokerMutex.Unlock() fake.UpdateBrokerStub = nil if fake.updateBrokerReturnsOnCall == nil { fake.updateBrokerReturnsOnCall = make(map[int]struct { result1 *types.Broker - result2 error + result2 string + result3 error }) } fake.updateBrokerReturnsOnCall[i] = struct { result1 *types.Broker - result2 error - }{result1, result2} + result2 string + result3 error + }{result1, result2, result3} } func (fake *FakeClient) UpdatePlatform(arg1 string, arg2 *types.Platform, arg3 *query.Parameters) (*types.Platform, error) { @@ -1783,14 +2200,18 @@ func (fake *FakeClient) UpdateVisibilityReturnsOnCall(i int, result1 *types.Visi func (fake *FakeClient) Invocations() map[string][][]interface{} { fake.invocationsMutex.RLock() defer fake.invocationsMutex.RUnlock() + fake.bindMutex.RLock() + defer fake.bindMutex.RUnlock() fake.callMutex.RLock() defer fake.callMutex.RUnlock() - fake.deleteBrokersMutex.RLock() - defer fake.deleteBrokersMutex.RUnlock() + fake.deleteBrokerMutex.RLock() + defer fake.deleteBrokerMutex.RUnlock() fake.deletePlatformsMutex.RLock() defer fake.deletePlatformsMutex.RUnlock() fake.deleteVisibilitiesMutex.RLock() defer fake.deleteVisibilitiesMutex.RUnlock() + fake.deprovisionMutex.RLock() + defer fake.deprovisionMutex.RUnlock() fake.getBindingByIDMutex.RLock() defer fake.getBindingByIDMutex.RUnlock() fake.getBrokerByIDMutex.RLock() @@ -1817,12 +2238,18 @@ func (fake *FakeClient) Invocations() map[string][][]interface{} { defer fake.listVisibilitiesMutex.RUnlock() fake.marketplaceMutex.RLock() defer fake.marketplaceMutex.RUnlock() + fake.provisionMutex.RLock() + defer fake.provisionMutex.RUnlock() fake.registerBrokerMutex.RLock() defer fake.registerBrokerMutex.RUnlock() fake.registerPlatformMutex.RLock() defer fake.registerPlatformMutex.RUnlock() fake.registerVisibilityMutex.RLock() defer fake.registerVisibilityMutex.RUnlock() + fake.statusMutex.RLock() + defer fake.statusMutex.RUnlock() + fake.unbindMutex.RLock() + defer fake.unbindMutex.RUnlock() fake.updateBrokerMutex.RLock() defer fake.updateBrokerMutex.RUnlock() fake.updatePlatformMutex.RLock() diff --git a/pkg/smclient/test/binding_test.go b/pkg/smclient/test/binding_test.go new file mode 100644 index 00000000..b226172b --- /dev/null +++ b/pkg/smclient/test/binding_test.go @@ -0,0 +1,317 @@ +package test + +import ( + "encoding/json" + "github.com/Peripli/service-manager-cli/pkg/smclient" + "github.com/Peripli/service-manager/pkg/web" + "net/http" + + "github.com/Peripli/service-manager-cli/pkg/types" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("Binding test", func() { + Describe("List service bindings", func() { + Context("when there are service bindings registered", func() { + BeforeEach(func() { + bindingsArray := []types.ServiceBinding{*binding} + bindings := types.ServiceBindings{ServiceBindings: bindingsArray} + responseBody, _ := json.Marshal(bindings) + + handlerDetails = []HandlerDetails{ + {Method: http.MethodGet, Path: web.ServiceBindingsURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, + } + }) + It("should return all", func() { + result, err := client.ListBindings(params) + Expect(err).ShouldNot(HaveOccurred()) + Expect(result.ServiceBindings).To(HaveLen(1)) + Expect(result.ServiceBindings[0]).To(Equal(*binding)) + }) + }) + + Context("when there are no service bindings registered", func() { + BeforeEach(func() { + bindingsArray := []types.ServiceBinding{} + bindings := types.ServiceBindings{ServiceBindings: bindingsArray} + responseBody, _ := json.Marshal(bindings) + + handlerDetails = []HandlerDetails{ + {Method: http.MethodGet, Path: web.ServiceBindingsURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, + } + }) + It("should return an empty array", func() { + result, err := client.ListBindings(params) + Expect(err).ShouldNot(HaveOccurred()) + Expect(result.ServiceBindings).To(HaveLen(0)) + }) + }) + + Context("when invalid status code is returned", func() { + BeforeEach(func() { + handlerDetails = []HandlerDetails{ + {Method: http.MethodGet, Path: web.ServiceBindingsURL, ResponseStatusCode: http.StatusCreated}, + } + }) + It("should handle status code != 200", func() { + _, err := client.ListBindings(params) + Expect(err).Should(HaveOccurred()) + verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + }) + }) + + Context("when invalid status code is returned", func() { + BeforeEach(func() { + handlerDetails = []HandlerDetails{ + {Method: http.MethodGet, Path: web.ServiceBindingsURL, ResponseStatusCode: http.StatusBadRequest}, + } + }) + It("should handle status code > 299", func() { + _, err := client.ListBindings(params) + Expect(err).Should(HaveOccurred()) + verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + }) + }) + }) + + Describe("Get service binding", func() { + Context("when there is binding with this id", func() { + BeforeEach(func() { + responseBody, _ := json.Marshal(binding) + handlerDetails = []HandlerDetails{ + {Method: http.MethodGet, Path: web.ServiceBindingsURL + "/", ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, + } + }) + It("should return it", func() { + result, err := client.GetBindingByID(binding.ID, params) + Expect(err).ShouldNot(HaveOccurred()) + Expect(result).To(Equal(binding)) + }) + }) + + Context("when there is no binding with this id", func() { + BeforeEach(func() { + handlerDetails = []HandlerDetails{ + {Method: http.MethodGet, Path: web.ServiceBindingsURL + "/", ResponseStatusCode: http.StatusNotFound}, + } + }) + It("should return 404", func() { + _, err := client.GetBindingByID(binding.ID, params) + Expect(err).Should(HaveOccurred()) + verifyErrorMsg(err.Error(), handlerDetails[0].Path+binding.ID, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + }) + }) + + Context("when invalid status code is returned", func() { + BeforeEach(func() { + handlerDetails = []HandlerDetails{ + {Method: http.MethodGet, Path: web.ServiceBindingsURL + "/", ResponseStatusCode: http.StatusCreated}, + } + }) + It("should handle status code != 200", func() { + _, err := client.GetBindingByID(binding.ID, params) + Expect(err).Should(HaveOccurred()) + verifyErrorMsg(err.Error(), handlerDetails[0].Path+binding.ID, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + }) + }) + + Context("when invalid status code is returned", func() { + BeforeEach(func() { + handlerDetails = []HandlerDetails{ + {Method: http.MethodGet, Path: web.ServiceBindingsURL + "/", ResponseStatusCode: http.StatusBadRequest}, + } + }) + It("should handle status code > 299", func() { + _, err := client.GetBindingByID(binding.ID, params) + Expect(err).Should(HaveOccurred()) + verifyErrorMsg(err.Error(), handlerDetails[0].Path+binding.ID, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + + }) + }) + }) + + Describe("Bind", func() { + Context("When valid binding is being created synchronously", func() { + BeforeEach(func() { + responseBody, _ := json.Marshal(binding) + handlerDetails = []HandlerDetails{ + {Method: http.MethodPost, Path: web.ServiceBindingsURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusCreated}, + } + }) + It("should provision successfully", func() { + responseBinding, location, err := client.Bind(binding, params) + + Expect(err).ShouldNot(HaveOccurred()) + Expect(location).Should(HaveLen(0)) + Expect(responseBinding).To(Equal(binding)) + }) + }) + + Context("When valid binding is being created asynchronously", func() { + var locationHeader string + BeforeEach(func() { + locationHeader = "test-location" + handlerDetails = []HandlerDetails{ + {Method: http.MethodPost, Path: web.ServiceBindingsURL, ResponseStatusCode: http.StatusAccepted, Headers: map[string]string{"Location": locationHeader}}, + } + }) + It("should receive operation location", func() { + responseBinding, location, err := client.Bind(binding, params) + + Expect(err).ShouldNot(HaveOccurred()) + Expect(location).Should(Equal(locationHeader)) + Expect(responseBinding).To(BeNil()) + }) + }) + + Context("When invalid binding is being returned by SM", func() { + BeforeEach(func() { + responseBody, _ := json.Marshal(struct { + Name bool `json:"name"` + }{ + Name: true, + }) + handlerDetails = []HandlerDetails{ + {Method: http.MethodPost, Path: web.ServiceBindingsURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusCreated}, + } + }) + It("should return error", func() { + responseBinding, location, err := client.Bind(binding, params) + + Expect(err).Should(HaveOccurred()) + Expect(location).Should(BeEmpty()) + Expect(responseBinding).To(BeNil()) + }) + }) + + Context("When invalid status code is returned by SM", func() { + Context("And status code is unsuccessful", func() { + BeforeEach(func() { + responseBody, _ := json.Marshal(binding) + handlerDetails = []HandlerDetails{ + {Method: http.MethodPost, Path: web.ServiceBindingsURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, + } + }) + It("should return error with status code", func() { + responseBinding, location, err := client.Bind(binding, params) + + Expect(err).Should(HaveOccurred()) + Expect(location).Should(BeEmpty()) + verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + Expect(responseBinding).To(BeNil()) + }) + }) + + Context("And status code is unsuccessful", func() { + BeforeEach(func() { + responseBody := []byte(`{ "description": "description", "error": "error"}`) + handlerDetails = []HandlerDetails{ + {Method: http.MethodPost, Path: web.ServiceBindingsURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusBadRequest}, + } + }) + It("should return error with url and description", func() { + responseBinding, location, err := client.Bind(binding, params) + + Expect(err).Should(HaveOccurred()) + Expect(location).Should(BeEmpty()) + verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + Expect(responseBinding).To(BeNil()) + }) + }) + + Context("And invalid response body", func() { + BeforeEach(func() { + responseBody := []byte(`{ "description": description", "error": "error"}`) + handlerDetails = []HandlerDetails{ + {Method: http.MethodPost, Path: web.ServiceBindingsURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusBadRequest}, + } + }) + It("should return error without url and description if invalid response body", func() { + responseBinding, location, err := client.Bind(binding, params) + + Expect(err).Should(HaveOccurred()) + Expect(location).Should(BeEmpty()) + + verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + Expect(responseBinding).To(BeNil()) + }) + }) + + }) + + Context("When invalid config is set", func() { + It("should return error", func() { + client = smclient.NewClient(fakeAuthClient, "invalidURL") + _, location, err := client.Bind(binding, params) + + Expect(err).Should(HaveOccurred()) + Expect(location).Should(BeEmpty()) + }) + }) + }) + + Describe("Unbind", func() { + Context("when an existing binding is being deleted synchronously", func() { + BeforeEach(func() { + responseBody := []byte("{}") + handlerDetails = []HandlerDetails{ + {Method: http.MethodDelete, Path: web.ServiceBindingsURL + "/", ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, + } + }) + It("should be successfully removed", func() { + location, err := client.Unbind(binding.ID, params) + Expect(err).ShouldNot(HaveOccurred()) + Expect(location).Should(BeEmpty()) + }) + }) + + Context("when an existing binding is being deleted asynchronously", func() { + var locationHeader string + BeforeEach(func() { + locationHeader = "location" + handlerDetails = []HandlerDetails{ + {Method: http.MethodDelete, Path: web.ServiceBindingsURL + "/", ResponseStatusCode: http.StatusAccepted, Headers: map[string]string{"Location": locationHeader}}, + } + }) + It("should be successfully removed", func() { + location, err := client.Unbind(binding.ID, params) + Expect(err).ShouldNot(HaveOccurred()) + Expect(location).Should(Equal(locationHeader)) + }) + }) + + Context("when service manager returns a non-expected status code", func() { + BeforeEach(func() { + responseBody := []byte("{}") + + handlerDetails = []HandlerDetails{ + {Method: http.MethodDelete, Path: web.ServiceBindingsURL + "/", ResponseBody: responseBody, ResponseStatusCode: http.StatusCreated}, + } + }) + It("should handle error", func() { + location, err := client.Unbind(binding.ID, params) + Expect(err).Should(HaveOccurred()) + Expect(location).Should(BeEmpty()) + verifyErrorMsg(err.Error(), handlerDetails[0].Path+binding.ID, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + }) + }) + + Context("when service manager returns a status code not found", func() { + BeforeEach(func() { + responseBody := []byte(`{ "description": "Broker not found" }`) + + handlerDetails = []HandlerDetails{ + {Method: http.MethodDelete, Path: web.ServiceBindingsURL + "/", ResponseBody: responseBody, ResponseStatusCode: http.StatusNotFound}, + } + }) + It("should handle error", func() { + location, err := client.Unbind(binding.ID, params) + Expect(err).Should(HaveOccurred()) + Expect(location).Should(BeEmpty()) + verifyErrorMsg(err.Error(), handlerDetails[0].Path+binding.ID, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + }) + }) + }) +}) diff --git a/pkg/smclient/test/broker_test.go b/pkg/smclient/test/broker_test.go new file mode 100644 index 00000000..b9520996 --- /dev/null +++ b/pkg/smclient/test/broker_test.go @@ -0,0 +1,384 @@ +package test + +import ( + "encoding/json" + + "github.com/Peripli/service-manager-cli/pkg/smclient" + "github.com/Peripli/service-manager/pkg/web" + "net/http" + + "github.com/Peripli/service-manager-cli/pkg/types" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("Broker test", func() { + Describe("List brokers", func() { + Context("when there are brokers registered", func() { + BeforeEach(func() { + brokersArray := []types.Broker{*broker} + brokers := types.Brokers{Brokers: brokersArray} + responseBody, _ := json.Marshal(brokers) + handlerDetails = []HandlerDetails{ + {Method: http.MethodGet, Path: web.ServiceBrokersURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, + } + }) + It("should return all", func() { + result, err := client.ListBrokers(params) + Expect(err).ShouldNot(HaveOccurred()) + Expect(result.Brokers).To(HaveLen(1)) + Expect(result.Brokers[0]).To(Equal(*broker)) + }) + }) + + Context("when there are no brokers registered", func() { + BeforeEach(func() { + brokersArray := []types.Broker{} + brokers := types.Brokers{Brokers: brokersArray} + responseBody, _ := json.Marshal(brokers) + handlerDetails = []HandlerDetails{ + {Method: http.MethodGet, Path: web.ServiceBrokersURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, + } + }) + It("should return an empty array", func() { + result, err := client.ListBrokers(params) + Expect(err).ShouldNot(HaveOccurred()) + Expect(result.Brokers).To(HaveLen(0)) + }) + }) + + Context("when invalid status code is returned", func() { + BeforeEach(func() { + handlerDetails = []HandlerDetails{ + {Method: http.MethodGet, Path: web.ServiceBrokersURL, ResponseStatusCode: http.StatusCreated}, + } + }) + It("should handle status code != 200", func() { + _, err := client.ListBrokers(params) + Expect(err).Should(HaveOccurred()) + verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + }) + }) + + Context("when invalid status code is returned", func() { + BeforeEach(func() { + handlerDetails = []HandlerDetails{ + {Method: http.MethodGet, Path: web.ServiceBrokersURL, ResponseStatusCode: http.StatusBadRequest}, + } + }) + It("should handle status code > 299", func() { + _, err := client.ListBrokers(params) + Expect(err).Should(HaveOccurred()) + verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + + }) + }) + }) + + Describe("Get broker", func() { + Context("when there is broker registered with this id", func() { + BeforeEach(func() { + responseBody, _ := json.Marshal(broker) + handlerDetails = []HandlerDetails{ + {Method: http.MethodGet, Path: web.ServiceBrokersURL + "/", ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, + } + }) + It("should return it", func() { + result, err := client.GetBrokerByID(broker.ID, params) + Expect(err).ShouldNot(HaveOccurred()) + Expect(result).To(Equal(broker)) + }) + }) + + Context("when there is no brokers registered", func() { + BeforeEach(func() { + handlerDetails = []HandlerDetails{ + {Method: http.MethodGet, Path: web.ServiceBrokersURL + "/", ResponseStatusCode: http.StatusNotFound}, + } + }) + It("should return 404", func() { + _, err := client.GetBrokerByID(broker.ID, params) + Expect(err).Should(HaveOccurred()) + verifyErrorMsg(err.Error(), handlerDetails[0].Path+broker.ID, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + }) + }) + + Context("when invalid status code is returned", func() { + BeforeEach(func() { + handlerDetails = []HandlerDetails{ + {Method: http.MethodGet, Path: web.ServiceBrokersURL + "/", ResponseStatusCode: http.StatusCreated}, + } + }) + It("should handle status code != 200", func() { + _, err := client.GetBrokerByID(broker.ID, params) + Expect(err).Should(HaveOccurred()) + verifyErrorMsg(err.Error(), handlerDetails[0].Path+broker.ID, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + }) + }) + + Context("when invalid status code is returned", func() { + BeforeEach(func() { + handlerDetails = []HandlerDetails{ + {Method: http.MethodGet, Path: web.ServiceBrokersURL + "/", ResponseStatusCode: http.StatusBadRequest}, + } + }) + It("should handle status code > 299", func() { + _, err := client.GetBrokerByID(broker.ID, params) + Expect(err).Should(HaveOccurred()) + verifyErrorMsg(err.Error(), handlerDetails[0].Path+broker.ID, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + + }) + }) + }) + + Describe("Register broker", func() { + Context("When valid broker is being registered synchronously", func() { + BeforeEach(func() { + responseBody, _ := json.Marshal(broker) + handlerDetails = []HandlerDetails{ + {Method: http.MethodPost, Path: web.ServiceBrokersURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusCreated}, + } + }) + It("should register successfully", func() { + responseBroker, location, err := client.RegisterBroker(broker, params) + + Expect(err).ShouldNot(HaveOccurred()) + Expect(location).Should(HaveLen(0)) + Expect(responseBroker).To(Equal(broker)) + }) + }) + + Context("When valid broker is being registered asynchronously", func() { + var locationHeader string + BeforeEach(func() { + locationHeader = "test-location" + handlerDetails = []HandlerDetails{ + {Method: http.MethodPost, Path: web.ServiceBrokersURL, ResponseStatusCode: http.StatusAccepted, Headers: map[string]string{"Location": locationHeader}}, + } + }) + It("should receive operation location", func() { + responseBroker, location, err := client.RegisterBroker(broker, params) + + Expect(err).ShouldNot(HaveOccurred()) + Expect(location).Should(Equal(locationHeader)) + Expect(responseBroker).To(BeNil()) + }) + }) + + Context("When invalid broker is being returned by SM", func() { + BeforeEach(func() { + responseBody, _ := json.Marshal(struct { + Name bool `json:"name"` + }{ + Name: true, + }) + handlerDetails = []HandlerDetails{ + {Method: http.MethodPost, Path: web.ServiceBrokersURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusCreated}, + } + }) + It("should return error", func() { + responseBroker, location, err := client.RegisterBroker(broker, params) + + Expect(err).Should(HaveOccurred()) + Expect(location).Should(BeEmpty()) + Expect(responseBroker).To(BeNil()) + }) + }) + + Context("When invalid status code is returned by SM", func() { + Context("And status code is unsuccessful", func() { + BeforeEach(func() { + responseBody, _ := json.Marshal(broker) + handlerDetails = []HandlerDetails{ + {Method: http.MethodPost, Path: web.ServiceBrokersURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, + } + }) + It("should return error with status code", func() { + responseBroker, location, err := client.RegisterBroker(broker, params) + + Expect(err).Should(HaveOccurred()) + Expect(location).Should(BeEmpty()) + verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + Expect(responseBroker).To(BeNil()) + }) + }) + + Context("And status code is unsuccessful", func() { + BeforeEach(func() { + responseBody := []byte(`{ "description": "description", "error": "error"}`) + handlerDetails = []HandlerDetails{ + {Method: http.MethodPost, Path: web.ServiceBrokersURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusBadRequest}, + } + }) + It("should return error with url and description", func() { + responseBroker, location, err := client.RegisterBroker(broker, params) + + Expect(err).Should(HaveOccurred()) + Expect(location).Should(BeEmpty()) + verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + Expect(responseBroker).To(BeNil()) + }) + }) + + Context("And invalid response body", func() { + BeforeEach(func() { + responseBody := []byte(`{ "description": description", "error": "error"}`) + handlerDetails = []HandlerDetails{ + {Method: http.MethodPost, Path: web.ServiceBrokersURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusBadRequest}, + } + }) + It("should return error without url and description if invalid response body", func() { + responseBroker, location, err := client.RegisterBroker(broker, params) + + Expect(err).Should(HaveOccurred()) + Expect(location).Should(BeEmpty()) + + verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + Expect(responseBroker).To(BeNil()) + }) + }) + + }) + + Context("When invalid config is set", func() { + It("should return error", func() { + client = smclient.NewClient(fakeAuthClient, "invalidURL") + _, location, err := client.RegisterBroker(broker, params) + + Expect(err).Should(HaveOccurred()) + Expect(location).Should(BeEmpty()) + }) + }) + }) + + Describe("Update brokers", func() { + Context("when an existing broker is being updated synchronously", func() { + BeforeEach(func() { + responseBody, _ := json.Marshal(broker) + + handlerDetails = []HandlerDetails{ + {Method: http.MethodPatch, Path: web.ServiceBrokersURL + "/", ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, + } + }) + It("should be successfully updated", func() { + updatedBroker, location, err := client.UpdateBroker("id", broker, params) + Expect(err).ShouldNot(HaveOccurred()) + Expect(location).Should(HaveLen(0)) + Expect(updatedBroker).To(Equal(broker)) + }) + }) + + Context("when an existing broker is being updated asynchronously", func() { + var locationHeader string + BeforeEach(func() { + locationHeader = "test-location" + handlerDetails = []HandlerDetails{ + {Method: http.MethodPatch, Path: web.ServiceBrokersURL + "/", ResponseStatusCode: http.StatusAccepted, Headers: map[string]string{"Location": locationHeader}}, + } + }) + It("should be successfully updated", func() { + updatedBroker, location, err := client.UpdateBroker("id", broker, params) + Expect(err).ShouldNot(HaveOccurred()) + Expect(location).Should(Equal(locationHeader)) + Expect(updatedBroker).To(BeNil()) + }) + }) + + Context("when a non-existing broker is being updated", func() { + BeforeEach(func() { + responseBody := []byte(`{"description": "Broker not found"}`) + + handlerDetails = []HandlerDetails{ + {Method: http.MethodPatch, Path: web.ServiceBrokersURL + "/", ResponseBody: responseBody, ResponseStatusCode: http.StatusNotFound}, + } + }) + It("should handle error", func() { + _, location, err := client.UpdateBroker("id", broker, params) + Expect(err).Should(HaveOccurred()) + Expect(location).Should(HaveLen(0)) + verifyErrorMsg(err.Error(), handlerDetails[0].Path+"id", handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + }) + }) + + Context("when service manager returns a non-expected status code", func() { + BeforeEach(func() { + responseBody := []byte("{}") + + handlerDetails = []HandlerDetails{ + {Method: http.MethodPatch, Path: web.ServiceBrokersURL + "/", ResponseBody: responseBody, ResponseStatusCode: http.StatusCreated}, + } + }) + It("should handle error", func() { + _, location, err := client.UpdateBroker("id", broker, params) + Expect(err).Should(HaveOccurred()) + Expect(location).Should(HaveLen(0)) + verifyErrorMsg(err.Error(), handlerDetails[0].Path+"id", handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + }) + }) + }) + + Describe("Delete broker", func() { + Context("when an existing broker is being deleted synchronously", func() { + BeforeEach(func() { + responseBody := []byte("{}") + + handlerDetails = []HandlerDetails{ + {Method: http.MethodDelete, Path: web.ServiceBrokersURL + "/", ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, + } + }) + It("should be successfully removed", func() { + location, err := client.DeleteBroker(broker.ID, params) + Expect(err).ShouldNot(HaveOccurred()) + Expect(location).Should(BeEmpty()) + }) + }) + + Context("when an existing broker is being deleted asynchronously", func() { + var locationHeader string + BeforeEach(func() { + locationHeader = "location" + handlerDetails = []HandlerDetails{ + {Method: http.MethodDelete, Path: web.ServiceBrokersURL + "/", ResponseStatusCode: http.StatusAccepted, Headers: map[string]string{"Location": locationHeader}}, + } + }) + It("should be successfully removed", func() { + location, err := client.DeleteBroker(broker.ID, params) + Expect(err).ShouldNot(HaveOccurred()) + Expect(location).Should(Equal(locationHeader)) + }) + }) + + Context("when service manager returns a non-expected status code", func() { + BeforeEach(func() { + responseBody := []byte("{}") + + handlerDetails = []HandlerDetails{ + {Method: http.MethodDelete, Path: web.ServiceBrokersURL + "/", ResponseBody: responseBody, ResponseStatusCode: http.StatusCreated}, + } + }) + It("should handle error", func() { + location, err := client.DeleteBroker(broker.ID, params) + Expect(err).Should(HaveOccurred()) + Expect(location).Should(BeEmpty()) + verifyErrorMsg(err.Error(), handlerDetails[0].Path+broker.ID, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + }) + }) + + Context("when service manager returns a status code not found", func() { + BeforeEach(func() { + responseBody := []byte(`{ "description": "Broker not found" }`) + + handlerDetails = []HandlerDetails{ + {Method: http.MethodDelete, Path: web.ServiceBrokersURL + "/", ResponseBody: responseBody, ResponseStatusCode: http.StatusNotFound}, + } + }) + It("should handle error", func() { + location, err := client.DeleteBroker(broker.ID, params) + Expect(err).Should(HaveOccurred()) + Expect(location).Should(BeEmpty()) + verifyErrorMsg(err.Error(), handlerDetails[0].Path+broker.ID, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + }) + }) + }) +}) diff --git a/pkg/smclient/test/client_test.go b/pkg/smclient/test/client_test.go new file mode 100644 index 00000000..b1267993 --- /dev/null +++ b/pkg/smclient/test/client_test.go @@ -0,0 +1,31 @@ +package test + +import ( + "github.com/Peripli/service-manager-cli/pkg/smclient" + "github.com/Peripli/service-manager/pkg/web" + "net/http" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("SM Client test", func() { + Describe("Test failing client authentication", func() { + + Context("When wrong token is used", func() { + BeforeEach(func() { + handlerDetails = []HandlerDetails{ + {Method: http.MethodGet, Path: web.ServiceBrokersURL}, + } + }) + It("should fail to authentication", func() { + fakeAuthClient.AccessToken = invalidToken + client = smclient.NewClient(fakeAuthClient, smServer.URL) + _, err := client.ListBrokers(params) + + Expect(err).Should(HaveOccurred()) + verifyErrorMsg(err.Error(), handlerDetails[0].Path, []byte{}, http.StatusUnauthorized) + }) + }) + }) +}) diff --git a/pkg/smclient/test/info_test.go b/pkg/smclient/test/info_test.go new file mode 100644 index 00000000..51256b6f --- /dev/null +++ b/pkg/smclient/test/info_test.go @@ -0,0 +1,86 @@ +package test + +import ( + "github.com/Peripli/service-manager/pkg/web" + "net/http" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("Info test", func() { + Context("when token issuer is set", func() { + BeforeEach(func() { + responseBody := []byte(`{"token_issuer_url": "http://uaa.com"}`) + + handlerDetails = []HandlerDetails{ + {Method: http.MethodGet, Path: web.InfoURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, + } + }) + It("should get the right issuer and default token basic auth", func() { + info, _ := client.GetInfo(params) + Expect(info.TokenIssuerURL).To(Equal("http://uaa.com")) + Expect(info.TokenBasicAuth).To(BeTrue()) // default value + }) + }) + + Context("when token basic auth is set", func() { + BeforeEach(func() { + responseBody := []byte(`{"token_issuer_url": "http://uaa.com", "token_basic_auth": false}`) + + handlerDetails = []HandlerDetails{ + {Method: http.MethodGet, Path: web.InfoURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, + } + }) + It("should get the right value", func() { + info, _ := client.GetInfo(params) + Expect(info.TokenIssuerURL).To(Equal("http://uaa.com")) + Expect(info.TokenBasicAuth).To(BeFalse()) + }) + }) + + Context("when invalid status code is returned", func() { + BeforeEach(func() { + responseBody := []byte(``) + + handlerDetails = []HandlerDetails{ + {Method: http.MethodGet, Path: web.InfoURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusNotFound}, + } + }) + It("should get an error", func() { + _, err := client.GetInfo(params) + Expect(err).Should(HaveOccurred()) + verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + + }) + }) + + Context("when invalid json is returned", func() { + BeforeEach(func() { + responseBody := []byte(`{"token_issuer":}`) + + handlerDetails = []HandlerDetails{ + {Method: http.MethodGet, Path: web.InfoURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, + } + }) + It("should get an error", func() { + _, err := client.GetInfo(params) + Expect(err).Should(HaveOccurred()) + }) + }) + + Context("with general parameter", func() { + BeforeEach(func() { + responseBody := []byte(`{"token_issuer_url": "http://uaa.com"}`) + handlerDetails = []HandlerDetails{ + {Method: http.MethodGet, Path: web.InfoURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, + } + }) + + It("should make request with these parameters", func() { + info, err := client.GetInfo(params) + Expect(err).ToNot(HaveOccurred()) + Expect(info).ToNot(BeNil()) + }) + }) +}) diff --git a/pkg/smclient/test/instance_test.go b/pkg/smclient/test/instance_test.go new file mode 100644 index 00000000..0bb5c6ad --- /dev/null +++ b/pkg/smclient/test/instance_test.go @@ -0,0 +1,318 @@ +package test + +import ( + "encoding/json" + "github.com/Peripli/service-manager-cli/pkg/smclient" + "net/http" + + "github.com/Peripli/service-manager/pkg/web" + + "github.com/Peripli/service-manager-cli/pkg/types" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("Instance test", func() { + Describe("List service instances", func() { + Context("when there are service instances registered", func() { + BeforeEach(func() { + instancesArray := []types.ServiceInstance{*instance} + instances := types.ServiceInstances{ServiceInstances: instancesArray} + responseBody, _ := json.Marshal(instances) + + handlerDetails = []HandlerDetails{ + {Method: http.MethodGet, Path: web.ServiceInstancesURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, + } + }) + It("should return all", func() { + result, err := client.ListInstances(params) + Expect(err).ShouldNot(HaveOccurred()) + Expect(result.ServiceInstances).To(HaveLen(1)) + Expect(result.ServiceInstances[0]).To(Equal(*instance)) + }) + }) + + Context("when there are no service instances registered", func() { + BeforeEach(func() { + instancesArray := []types.ServiceInstance{} + instances := types.ServiceInstances{ServiceInstances: instancesArray} + responseBody, _ := json.Marshal(instances) + + handlerDetails = []HandlerDetails{ + {Method: http.MethodGet, Path: web.ServiceInstancesURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, + } + }) + It("should return an empty array", func() { + result, err := client.ListInstances(params) + Expect(err).ShouldNot(HaveOccurred()) + Expect(result.ServiceInstances).To(HaveLen(0)) + }) + }) + + Context("when invalid status code is returned", func() { + BeforeEach(func() { + handlerDetails = []HandlerDetails{ + {Method: http.MethodGet, Path: web.ServiceInstancesURL, ResponseStatusCode: http.StatusCreated}, + } + }) + It("should handle status code != 200", func() { + _, err := client.ListInstances(params) + Expect(err).Should(HaveOccurred()) + verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + }) + }) + + Context("when invalid status code is returned", func() { + BeforeEach(func() { + handlerDetails = []HandlerDetails{ + {Method: http.MethodGet, Path: web.ServiceInstancesURL, ResponseStatusCode: http.StatusBadRequest}, + } + }) + It("should handle status code > 299", func() { + _, err := client.ListInstances(params) + Expect(err).Should(HaveOccurred()) + verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + }) + }) + }) + + Describe("Get service instance", func() { + Context("when there is instance with this id", func() { + BeforeEach(func() { + responseBody, _ := json.Marshal(instance) + handlerDetails = []HandlerDetails{ + {Method: http.MethodGet, Path: web.ServiceInstancesURL + "/", ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, + } + }) + It("should return it", func() { + result, err := client.GetInstanceByID(instance.ID, params) + Expect(err).ShouldNot(HaveOccurred()) + Expect(result).To(Equal(instance)) + }) + }) + + Context("when there is no instance with this id", func() { + BeforeEach(func() { + handlerDetails = []HandlerDetails{ + {Method: http.MethodGet, Path: web.ServiceInstancesURL + "/", ResponseStatusCode: http.StatusNotFound}, + } + }) + It("should return 404", func() { + _, err := client.GetInstanceByID(instance.ID, params) + Expect(err).Should(HaveOccurred()) + verifyErrorMsg(err.Error(), handlerDetails[0].Path+instance.ID, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + }) + }) + + Context("when invalid status code is returned", func() { + BeforeEach(func() { + handlerDetails = []HandlerDetails{ + {Method: http.MethodGet, Path: web.ServiceInstancesURL + "/", ResponseStatusCode: http.StatusCreated}, + } + }) + It("should handle status code != 200", func() { + _, err := client.GetInstanceByID(instance.ID, params) + Expect(err).Should(HaveOccurred()) + verifyErrorMsg(err.Error(), handlerDetails[0].Path+instance.ID, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + }) + }) + + Context("when invalid status code is returned", func() { + BeforeEach(func() { + handlerDetails = []HandlerDetails{ + {Method: http.MethodGet, Path: web.ServiceInstancesURL + "/", ResponseStatusCode: http.StatusBadRequest}, + } + }) + It("should handle status code > 299", func() { + _, err := client.GetInstanceByID(instance.ID, params) + Expect(err).Should(HaveOccurred()) + verifyErrorMsg(err.Error(), handlerDetails[0].Path+instance.ID, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + + }) + }) + }) + + Describe("Provision", func() { + Context("When valid instance is being provisioned synchronously", func() { + BeforeEach(func() { + responseBody, _ := json.Marshal(instance) + handlerDetails = []HandlerDetails{ + {Method: http.MethodPost, Path: web.ServiceInstancesURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusCreated}, + } + }) + It("should provision successfully", func() { + responseInstance, location, err := client.Provision(instance, params) + + Expect(err).ShouldNot(HaveOccurred()) + Expect(location).Should(HaveLen(0)) + Expect(responseInstance).To(Equal(instance)) + }) + }) + + Context("When valid instance is being provisioned asynchronously", func() { + var locationHeader string + BeforeEach(func() { + locationHeader = "test-location" + handlerDetails = []HandlerDetails{ + {Method: http.MethodPost, Path: web.ServiceInstancesURL, ResponseStatusCode: http.StatusAccepted, Headers: map[string]string{"Location": locationHeader}}, + } + }) + It("should receive operation location", func() { + responseInstance, location, err := client.Provision(instance, params) + + Expect(err).ShouldNot(HaveOccurred()) + Expect(location).Should(Equal(locationHeader)) + Expect(responseInstance).To(BeNil()) + }) + }) + + Context("When invalid instance is being returned by SM", func() { + BeforeEach(func() { + responseBody, _ := json.Marshal(struct { + Name bool `json:"name"` + }{ + Name: true, + }) + handlerDetails = []HandlerDetails{ + {Method: http.MethodPost, Path: web.ServiceInstancesURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusCreated}, + } + }) + It("should return error", func() { + responseInstance, location, err := client.Provision(instance, params) + + Expect(err).Should(HaveOccurred()) + Expect(location).Should(BeEmpty()) + Expect(responseInstance).To(BeNil()) + }) + }) + + Context("When invalid status code is returned by SM", func() { + Context("And status code is unsuccessful", func() { + BeforeEach(func() { + responseBody, _ := json.Marshal(instance) + handlerDetails = []HandlerDetails{ + {Method: http.MethodPost, Path: web.ServiceInstancesURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, + } + }) + It("should return error with status code", func() { + responseInstance, location, err := client.Provision(instance, params) + + Expect(err).Should(HaveOccurred()) + Expect(location).Should(BeEmpty()) + verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + Expect(responseInstance).To(BeNil()) + }) + }) + + Context("And status code is unsuccessful", func() { + BeforeEach(func() { + responseBody := []byte(`{ "description": "description", "error": "error"}`) + handlerDetails = []HandlerDetails{ + {Method: http.MethodPost, Path: web.ServiceInstancesURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusBadRequest}, + } + }) + It("should return error with url and description", func() { + responseInstance, location, err := client.Provision(instance, params) + + Expect(err).Should(HaveOccurred()) + Expect(location).Should(BeEmpty()) + verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + Expect(responseInstance).To(BeNil()) + }) + }) + + Context("And invalid response body", func() { + BeforeEach(func() { + responseBody := []byte(`{ "description": description", "error": "error"}`) + handlerDetails = []HandlerDetails{ + {Method: http.MethodPost, Path: web.ServiceInstancesURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusBadRequest}, + } + }) + It("should return error without url and description if invalid response body", func() { + responseInstance, location, err := client.Provision(instance, params) + + Expect(err).Should(HaveOccurred()) + Expect(location).Should(BeEmpty()) + + verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + Expect(responseInstance).To(BeNil()) + }) + }) + + }) + + Context("When invalid config is set", func() { + It("should return error", func() { + client = smclient.NewClient(fakeAuthClient, "invalidURL") + _, location, err := client.Provision(instance, params) + + Expect(err).Should(HaveOccurred()) + Expect(location).Should(BeEmpty()) + }) + }) + }) + + Describe("Deprovision", func() { + Context("when an existing instance is being deleted synchronously", func() { + BeforeEach(func() { + responseBody := []byte("{}") + handlerDetails = []HandlerDetails{ + {Method: http.MethodDelete, Path: web.ServiceInstancesURL + "/", ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, + } + }) + It("should be successfully removed", func() { + location, err := client.Deprovision(instance.ID, params) + Expect(err).ShouldNot(HaveOccurred()) + Expect(location).Should(BeEmpty()) + }) + }) + + Context("when an existing instance is being deleted asynchronously", func() { + var locationHeader string + BeforeEach(func() { + locationHeader = "location" + handlerDetails = []HandlerDetails{ + {Method: http.MethodDelete, Path: web.ServiceInstancesURL + "/", ResponseStatusCode: http.StatusAccepted, Headers: map[string]string{"Location": locationHeader}}, + } + }) + It("should be successfully removed", func() { + location, err := client.Deprovision(instance.ID, params) + Expect(err).ShouldNot(HaveOccurred()) + Expect(location).Should(Equal(locationHeader)) + }) + }) + + Context("when service manager returns a non-expected status code", func() { + BeforeEach(func() { + responseBody := []byte("{}") + + handlerDetails = []HandlerDetails{ + {Method: http.MethodDelete, Path: web.ServiceInstancesURL + "/", ResponseBody: responseBody, ResponseStatusCode: http.StatusCreated}, + } + }) + It("should handle error", func() { + location, err := client.Deprovision(instance.ID, params) + Expect(err).Should(HaveOccurred()) + Expect(location).Should(BeEmpty()) + verifyErrorMsg(err.Error(), handlerDetails[0].Path+instance.ID, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + }) + }) + + Context("when service manager returns a status code not found", func() { + BeforeEach(func() { + responseBody := []byte(`{ "description": "Broker not found" }`) + + handlerDetails = []HandlerDetails{ + {Method: http.MethodDelete, Path: web.ServiceInstancesURL + "/", ResponseBody: responseBody, ResponseStatusCode: http.StatusNotFound}, + } + }) + It("should handle error", func() { + location, err := client.Deprovision(instance.ID, params) + Expect(err).Should(HaveOccurred()) + Expect(location).Should(BeEmpty()) + verifyErrorMsg(err.Error(), handlerDetails[0].Path+instance.ID, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + }) + }) + }) +}) diff --git a/pkg/smclient/test/label_test.go b/pkg/smclient/test/label_test.go new file mode 100644 index 00000000..97968943 --- /dev/null +++ b/pkg/smclient/test/label_test.go @@ -0,0 +1,43 @@ +package test + +import ( + "github.com/Peripli/service-manager-cli/pkg/smclient" + "net/http" + + "github.com/Peripli/service-manager/pkg/web" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("Label test", func() { + Context("when valid label change is sent", func() { + BeforeEach(func() { + handlerDetails = []HandlerDetails{{Method: http.MethodPatch, Path: web.ServiceBrokersURL + "/", ResponseStatusCode: http.StatusOK}} + }) + It("should label resource sucessfully", func() { + + err := client.Label(web.ServiceBrokersURL, "id", labelChanges, params) + Expect(err).ShouldNot(HaveOccurred()) + }) + }) + + Context("when invalid status code is returned", func() { + BeforeEach(func() { + handlerDetails = []HandlerDetails{{Method: http.MethodPatch, Path: web.ServiceBrokersURL + "/", ResponseStatusCode: http.StatusNotFound}} + }) + It("should return error", func() { + err := client.Label(web.ServiceBrokersURL, "id", labelChanges, params) + Expect(err).Should(HaveOccurred()) + verifyErrorMsg(err.Error(), handlerDetails[0].Path+"id", handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + }) + }) + + Context("When invalid config is set", func() { + It("should return error", func() { + client = smclient.NewClient(fakeAuthClient, "invalidURL") + err := client.Label(web.ServiceBrokersURL, "id", labelChanges, params) + Expect(err).Should(HaveOccurred()) + }) + }) +}) diff --git a/pkg/smclient/test/marketplace_test.go b/pkg/smclient/test/marketplace_test.go new file mode 100644 index 00000000..32c33660 --- /dev/null +++ b/pkg/smclient/test/marketplace_test.go @@ -0,0 +1,80 @@ +package test + +import ( + "encoding/json" + "github.com/Peripli/service-manager/pkg/web" + "net/http" + + "github.com/Peripli/service-manager-cli/pkg/types" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("Marketplace test", func() { + Context("when there are offerings provided", func() { + BeforeEach(func() { + offerings := types.ServiceOfferings{ServiceOfferings: []types.ServiceOffering{*initialOffering}} + offeringResponseBody, _ := json.Marshal(offerings) + + plans := types.ServicePlans{ServicePlans: []types.ServicePlan{*plan}} + plansResponseBody, _ := json.Marshal(plans) + + brokerResponseBody, _ := json.Marshal(broker) + + handlerDetails = []HandlerDetails{ + {Method: http.MethodGet, Path: web.ServiceOfferingsURL, ResponseBody: offeringResponseBody, ResponseStatusCode: http.StatusOK}, + {Method: http.MethodGet, Path: web.ServicePlansURL, ResponseBody: plansResponseBody, ResponseStatusCode: http.StatusOK}, + {Method: http.MethodGet, Path: web.ServiceBrokersURL + "/", ResponseBody: brokerResponseBody, ResponseStatusCode: http.StatusOK}, + } + }) + It("should return all with plans and broker name populated", func() { + result, err := client.Marketplace(params) + Expect(err).ShouldNot(HaveOccurred()) + Expect(result.ServiceOfferings).To(HaveLen(1)) + Expect(result.ServiceOfferings[0]).To(Equal(*resultOffering)) + }) + }) + + Context("when there are no offerings provided", func() { + BeforeEach(func() { + offerings := types.ServiceOfferings{} + offeringResponseBody, _ := json.Marshal(offerings) + + handlerDetails = []HandlerDetails{ + {Method: http.MethodGet, Path: web.ServiceOfferingsURL, ResponseBody: offeringResponseBody, ResponseStatusCode: http.StatusOK}, + } + }) + It("should return an empty array", func() { + result, err := client.Marketplace(params) + Expect(err).ShouldNot(HaveOccurred()) + Expect(result.ServiceOfferings).To(HaveLen(0)) + }) + }) + + Context("when invalid status code is returned", func() { + BeforeEach(func() { + handlerDetails = []HandlerDetails{ + {Method: http.MethodGet, Path: web.ServiceOfferingsURL, ResponseStatusCode: http.StatusCreated}, + } + }) + It("should handle status code != 200", func() { + _, err := client.Marketplace(params) + Expect(err).Should(HaveOccurred()) + verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + }) + }) + + Context("when invalid status code is returned", func() { + BeforeEach(func() { + handlerDetails = []HandlerDetails{ + {Method: http.MethodGet, Path: web.ServiceOfferingsURL, ResponseStatusCode: http.StatusBadRequest}, + } + }) + It("should handle status code > 299", func() { + _, err := client.Marketplace(params) + Expect(err).Should(HaveOccurred()) + verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + }) + }) +}) diff --git a/pkg/smclient/test/platform_test.go b/pkg/smclient/test/platform_test.go new file mode 100644 index 00000000..68d0057f --- /dev/null +++ b/pkg/smclient/test/platform_test.go @@ -0,0 +1,276 @@ +package test + +import ( + "encoding/json" + "github.com/Peripli/service-manager-cli/pkg/smclient" + "net/http" + + "github.com/Peripli/service-manager/pkg/web" + + "github.com/Peripli/service-manager-cli/pkg/types" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("Platform test", func() { + platform := &types.Platform{ + ID: "platformID", + Name: "cfeu10", + Type: "cf", + Description: "Test platform", + } + + Describe("List platforms", func() { + Context("when there are platforms registered", func() { + BeforeEach(func() { + platformsArray := []types.Platform{*platform} + platforms := types.Platforms{Platforms: platformsArray} + responseBody, _ := json.Marshal(platforms) + + handlerDetails = []HandlerDetails{ + {Method: http.MethodGet, Path: web.PlatformsURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, + } + }) + It("should return all", func() { + result, err := client.ListPlatforms(params) + Expect(err).ShouldNot(HaveOccurred()) + Expect(result.Platforms).To(HaveLen(1)) + Expect(result.Platforms[0]).To(Equal(*platform)) + }) + }) + + Context("when there are no platforms registered", func() { + BeforeEach(func() { + platformsArray := []types.Platform{} + platforms := types.Platforms{Platforms: platformsArray} + responseBody, _ := json.Marshal(platforms) + + handlerDetails = []HandlerDetails{ + {Method: http.MethodGet, Path: web.PlatformsURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, + } + }) + It("should return an empty array", func() { + result, err := client.ListPlatforms(params) + Expect(err).ShouldNot(HaveOccurred()) + Expect(result.Platforms).To(HaveLen(0)) + }) + }) + + Context("when invalid status code is returned", func() { + BeforeEach(func() { + handlerDetails = []HandlerDetails{ + {Method: http.MethodGet, Path: web.PlatformsURL, ResponseStatusCode: http.StatusCreated}, + } + }) + It("should handle status code != 200", func() { + _, err := client.ListPlatforms(params) + Expect(err).Should(HaveOccurred()) + verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + }) + }) + Context("when invalid status code is returned", func() { + BeforeEach(func() { + handlerDetails = []HandlerDetails{ + {Method: http.MethodGet, Path: web.PlatformsURL, ResponseStatusCode: http.StatusBadRequest}, + } + }) + It("should handle status code > 299", func() { + _, err := client.ListPlatforms(params) + Expect(err).Should(HaveOccurred()) + verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + }) + }) + }) + + Describe("Register platform", func() { + Context("When valid platform is being registered", func() { + BeforeEach(func() { + responseBody, _ := json.Marshal(platform) + handlerDetails = []HandlerDetails{ + {Method: http.MethodPost, Path: web.PlatformsURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusCreated}, + } + }) + It("should register successfully", func() { + responsePlatform, err := client.RegisterPlatform(platform, params) + + Expect(err).ShouldNot(HaveOccurred()) + Expect(responsePlatform).To(Equal(platform)) + }) + }) + + Context("When invalid platform is returned by SM", func() { + BeforeEach(func() { + responseBody, _ := json.Marshal(struct { + Name bool `json:"name"` + }{ + Name: true, + }) + handlerDetails = []HandlerDetails{ + {Method: http.MethodPost, Path: web.PlatformsURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusCreated}, + } + }) + It("should return error", func() { + responsePlatform, err := client.RegisterPlatform(platform, params) + + Expect(err).Should(HaveOccurred()) + Expect(responsePlatform).To(BeNil()) + }) + }) + + Context("When invalid status code is returned by SM", func() { + Context("And status code is successful", func() { + BeforeEach(func() { + responseBody, _ := json.Marshal(platform) + handlerDetails = []HandlerDetails{ + {Method: http.MethodPost, Path: web.PlatformsURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, + } + }) + It("should return error with status code", func() { + responsePlatform, err := client.RegisterPlatform(platform, params) + + Expect(err).Should(HaveOccurred()) + verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + Expect(responsePlatform).To(BeNil()) + }) + }) + + Context("And status code is unsuccessful", func() { + BeforeEach(func() { + responseBody := []byte(`{ "description": "error"}`) + handlerDetails = []HandlerDetails{ + {Method: http.MethodPost, Path: web.PlatformsURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusBadRequest}, + } + }) + It("should return error with url and description", func() { + responsePlatform, err := client.RegisterPlatform(platform, params) + + Expect(err).Should(HaveOccurred()) + verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + Expect(responsePlatform).To(BeNil()) + }) + }) + + Context("And response body is invalid", func() { + BeforeEach(func() { + responseBody := []byte(`{ "description": description", "error": "error"}`) + handlerDetails = []HandlerDetails{ + {Method: http.MethodPost, Path: web.PlatformsURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusBadRequest}, + } + }) + It("should return error without url and description if invalid response body", func() { + responsePlatform, err := client.RegisterPlatform(platform, params) + + Expect(err).Should(HaveOccurred()) + verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + Expect(responsePlatform).To(BeNil()) + }) + }) + }) + + Context("When invalid config is set", func() { + It("should return error", func() { + client = smclient.NewClient(fakeAuthClient, "invalidURL") + _, err := client.RegisterPlatform(platform, params) + + Expect(err).Should(HaveOccurred()) + }) + }) + }) + + Describe("Update platforms", func() { + Context("when an existing platform is being updated", func() { + BeforeEach(func() { + responseBody, _ := json.Marshal(platform) + + handlerDetails = []HandlerDetails{ + {Method: http.MethodPatch, Path: web.PlatformsURL + "/", ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, + } + }) + It("should be successfully removed", func() { + updatedPlatform, err := client.UpdatePlatform("id", platform, params) + Expect(err).ShouldNot(HaveOccurred()) + Expect(updatedPlatform).To(Equal(platform)) + }) + }) + + Context("when a non-existing platform is being updated", func() { + BeforeEach(func() { + responseBody := []byte(`{"description": "Platform not found"}`) + + handlerDetails = []HandlerDetails{ + {Method: http.MethodPatch, Path: web.PlatformsURL + "/", ResponseBody: responseBody, ResponseStatusCode: http.StatusNotFound}, + } + }) + It("should handle error", func() { + _, err := client.UpdatePlatform("id", platform, params) + Expect(err).Should(HaveOccurred()) + verifyErrorMsg(err.Error(), handlerDetails[0].Path+"id", handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + }) + }) + + Context("when service manager returns a non-expected status code", func() { + BeforeEach(func() { + responseBody := []byte("{}") + + handlerDetails = []HandlerDetails{ + {Method: http.MethodPatch, Path: web.PlatformsURL + "/", ResponseBody: responseBody, ResponseStatusCode: http.StatusCreated}, + } + }) + It("should handle error", func() { + _, err := client.UpdatePlatform("id", platform, params) + Expect(err).Should(HaveOccurred()) + verifyErrorMsg(err.Error(), handlerDetails[0].Path+"id", handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + }) + }) + }) + + Describe("Delete platforms", func() { + Context("when an existing platform is being deleted", func() { + BeforeEach(func() { + responseBody := []byte("{}") + + handlerDetails = []HandlerDetails{ + {Method: http.MethodDelete, Path: web.PlatformsURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, + } + }) + It("should be successfully removed", func() { + params.FieldQuery = append(params.FieldQuery, "id eq 'id'") + err := client.DeletePlatforms(params) + Expect(err).ShouldNot(HaveOccurred()) + }) + }) + + Context("when service manager returns a non-expected status code", func() { + BeforeEach(func() { + responseBody := []byte("{}") + + handlerDetails = []HandlerDetails{ + {Method: http.MethodDelete, Path: web.PlatformsURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusCreated}, + } + }) + It("should handle error", func() { + params.FieldQuery = append(params.FieldQuery, "id eq 'id'") + err := client.DeletePlatforms(params) + Expect(err).Should(HaveOccurred()) + verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + }) + }) + + Context("when service manager returns a status code not found", func() { + BeforeEach(func() { + responseBody := []byte(`{ "description": "Platform not found" }`) + + handlerDetails = []HandlerDetails{ + {Method: http.MethodDelete, Path: web.PlatformsURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusNotFound}, + } + }) + It("should handle error", func() { + params.FieldQuery = append(params.FieldQuery, "id eq 'id'") + err := client.DeletePlatforms(params) + Expect(err).Should(HaveOccurred()) + verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + }) + }) + }) +}) diff --git a/pkg/smclient/test/smclient_suite_test.go b/pkg/smclient/test/smclient_suite_test.go new file mode 100644 index 00000000..6cab21f3 --- /dev/null +++ b/pkg/smclient/test/smclient_suite_test.go @@ -0,0 +1,155 @@ +package test + +import ( + "encoding/json" + "fmt" + "github.com/Peripli/service-manager-cli/pkg/smclient" + "net/http" + "net/http/httptest" + "testing" + + cliquery "github.com/Peripli/service-manager-cli/pkg/query" + + "github.com/Peripli/service-manager-cli/pkg/types" + smtypes "github.com/Peripli/service-manager/pkg/types" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +type FakeAuthClient struct { + AccessToken string + requestURI string +} + +func (c *FakeAuthClient) Do(req *http.Request) (*http.Response, error) { + req.Header.Set("Authorization", "Bearer "+c.AccessToken) + c.requestURI = req.URL.RequestURI() + return http.DefaultClient.Do(req) +} + +type HandlerDetails struct { + Method string + Path string + ResponseBody []byte + ResponseStatusCode int + Headers map[string]string +} + +func TestSMClient(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "") +} + +var ( + client smclient.Client + handlerDetails []HandlerDetails + validToken = "valid-token" + invalidToken = "invalid-token" + smServer *httptest.Server + fakeAuthClient *FakeAuthClient + params *cliquery.Parameters + + broker = &types.Broker{ + ID: "broker-id", + Name: "test-broker", + URL: "http://test-url.com", + Credentials: &types.Credentials{Basic: types.Basic{User: "test user", Password: "test password"}}, + } + + initialOffering = &types.ServiceOffering{ + ID: "offeringID", + Name: "initial-offering", + Description: "Some description", + BrokerID: "id", + } + + plan = &types.ServicePlan{ + ID: "planID", + Name: "plan-1", + Description: "Sample Plan", + ServiceOfferingID: "offeringID", + } + + resultOffering = &types.ServiceOffering{ + ID: "offeringID", + Name: "initial-offering", + Description: "Some description", + Plans: []types.ServicePlan{*plan}, + BrokerID: "id", + BrokerName: "test-broker", + } + + instance = &types.ServiceInstance{ + ID: "instanceID", + Name: "instance1", + ServicePlanID: "service_plan_id", + PlatformID: "platform_id", + Context: json.RawMessage("{}"), + } + + binding = &types.ServiceBinding{ + ID: "instanceID", + Name: "instance1", + ServiceInstanceID: "service_instance_id", + } + + operation = &types.Operation{ + ID: "operation-id", + Type: "create", + State: "failed", + ResourceID: "broker-id", + } + + labelChanges = &types.LabelChanges{ + LabelChanges: []*smtypes.LabelChange{ + {Key: "key", Operation: smtypes.LabelOperation("add"), Values: []string{"val1", "val2"}}, + }, + } +) + +var createSMHandler = func() http.Handler { + mux := http.NewServeMux() + for i := range handlerDetails { + v := handlerDetails[i] + mux.HandleFunc(v.Path, func(response http.ResponseWriter, req *http.Request) { + if v.Method != req.Method { + return + } + for key, value := range v.Headers { + response.Header().Set(key, value) + } + authorization := req.Header.Get("Authorization") + if authorization != "Bearer "+validToken { + response.WriteHeader(http.StatusUnauthorized) + response.Write([]byte("")) + return + } + response.WriteHeader(v.ResponseStatusCode) + response.Write(v.ResponseBody) + }) + } + return mux +} + +var verifyErrorMsg = func(errorMsg, path string, body []byte, statusCode int) { + Expect(errorMsg).To(ContainSubstring(smclient.BuildURL(smServer.URL+path, params))) + Expect(errorMsg).To(ContainSubstring(string(body))) + Expect(errorMsg).To(ContainSubstring(fmt.Sprintf("StatusCode: %d", statusCode))) +} + +var _ = BeforeEach(func() { + params = &cliquery.Parameters{ + GeneralParams: []string{"key=value"}, + } +}) + +var _ = AfterEach(func() { + Expect(fakeAuthClient.requestURI).Should(ContainSubstring("key=value"), fmt.Sprintf("Request URI %s should contain ?key=value", fakeAuthClient.requestURI)) +}) + +var _ = JustBeforeEach(func() { + smServer = httptest.NewServer(createSMHandler()) + fakeAuthClient = &FakeAuthClient{AccessToken: validToken} + client = smclient.NewClient(fakeAuthClient, smServer.URL) +}) diff --git a/pkg/smclient/test/status_test.go b/pkg/smclient/test/status_test.go new file mode 100644 index 00000000..4b4e76e4 --- /dev/null +++ b/pkg/smclient/test/status_test.go @@ -0,0 +1,66 @@ +package test + +import ( + "encoding/json" + "github.com/Peripli/service-manager/pkg/web" + "net/http" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("Status test", func() { + Context("when there is operation registered with this id", func() { + BeforeEach(func() { + responseBody, _ := json.Marshal(operation) + handlerDetails = []HandlerDetails{ + {Method: http.MethodGet, Path: web.ServiceBrokersURL + "/", ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, + } + }) + It("should return it", func() { + result, err := client.Status(web.ServiceBrokersURL+"/"+broker.ID+"/"+operation.ID, params) + Expect(err).ShouldNot(HaveOccurred()) + Expect(result).To(Equal(operation)) + }) + }) + + Context("when there is no operation with this id", func() { + BeforeEach(func() { + handlerDetails = []HandlerDetails{ + {Method: http.MethodGet, Path: web.ServiceBrokersURL + "/", ResponseStatusCode: http.StatusNotFound}, + } + }) + It("should return 404", func() { + _, err := client.Status(web.ServiceBrokersURL+"/"+broker.ID+"/"+operation.ID, params) + Expect(err).Should(HaveOccurred()) + verifyErrorMsg(err.Error(), web.ServiceBrokersURL+"/"+broker.ID+"/"+operation.ID, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + }) + }) + + Context("when invalid status code is returned", func() { + BeforeEach(func() { + handlerDetails = []HandlerDetails{ + {Method: http.MethodGet, Path: web.ServiceBrokersURL + "/", ResponseStatusCode: http.StatusCreated}, + } + }) + It("should handle status code != 200", func() { + _, err := client.Status(web.ServiceBrokersURL+"/"+broker.ID+"/"+operation.ID, params) + Expect(err).Should(HaveOccurred()) + verifyErrorMsg(err.Error(), web.ServiceBrokersURL+"/"+broker.ID+"/"+operation.ID, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + }) + }) + + Context("when invalid status code is returned", func() { + BeforeEach(func() { + handlerDetails = []HandlerDetails{ + {Method: http.MethodGet, Path: web.ServiceBrokersURL + "/", ResponseStatusCode: http.StatusBadRequest}, + } + }) + It("should handle status code > 299", func() { + _, err := client.Status(web.ServiceBrokersURL+"/"+broker.ID+"/"+operation.ID, params) + Expect(err).Should(HaveOccurred()) + verifyErrorMsg(err.Error(), web.ServiceBrokersURL+"/"+broker.ID+"/"+operation.ID, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + + }) + }) +}) diff --git a/pkg/smclient/test/visibility_test.go b/pkg/smclient/test/visibility_test.go new file mode 100644 index 00000000..0c9dc46f --- /dev/null +++ b/pkg/smclient/test/visibility_test.go @@ -0,0 +1,279 @@ +package test + +import ( + "encoding/json" + "github.com/Peripli/service-manager-cli/pkg/smclient" + "github.com/Peripli/service-manager/pkg/web" + "net/http" + + "github.com/Peripli/service-manager-cli/pkg/types" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("Visibility test", func() { + + visibility := &types.Visibility{ + ID: "visibilityID", + PlatformID: "platformID", + ServicePlanID: "planID", + } + + Describe("List Visibilities", func() { + Context("when there are visibilities registered", func() { + BeforeEach(func() { + visibilitiesArray := []types.Visibility{*visibility} + visibilities := types.Visibilities{Visibilities: visibilitiesArray} + responseBody, _ := json.Marshal(visibilities) + + handlerDetails = []HandlerDetails{ + {Method: http.MethodGet, Path: web.VisibilitiesURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, + } + }) + It("should return all", func() { + result, err := client.ListVisibilities(params) + Expect(err).ShouldNot(HaveOccurred()) + Expect(result.Visibilities).To(HaveLen(1)) + Expect(result.Visibilities[0]).To(Equal(*visibility)) + }) + }) + + Context("when there are no visibilities registered", func() { + BeforeEach(func() { + visibilitiesArray := []types.Visibility{} + visibilities := types.Visibilities{Visibilities: visibilitiesArray} + responseBody, _ := json.Marshal(visibilities) + + handlerDetails = []HandlerDetails{ + {Method: http.MethodGet, Path: web.VisibilitiesURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, + } + }) + It("should return an empty array", func() { + result, err := client.ListVisibilities(params) + Expect(err).ShouldNot(HaveOccurred()) + Expect(result.Visibilities).To(HaveLen(0)) + }) + }) + + Context("when invalid status code is returned", func() { + BeforeEach(func() { + handlerDetails = []HandlerDetails{ + {Method: http.MethodGet, Path: web.VisibilitiesURL, ResponseStatusCode: http.StatusCreated}, + } + }) + It("should handle status code != 200", func() { + _, err := client.ListVisibilities(params) + Expect(err).Should(HaveOccurred()) + verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + }) + }) + + Context("when invalid status code is returned", func() { + BeforeEach(func() { + handlerDetails = []HandlerDetails{ + {Method: http.MethodGet, Path: web.VisibilitiesURL, ResponseStatusCode: http.StatusBadRequest}, + } + }) + It("should handle status code > 299", func() { + _, err := client.ListVisibilities(params) + Expect(err).Should(HaveOccurred()) + verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + }) + }) + }) + + Describe("Register Visibility", func() { + Context("When valid visibility is being registered", func() { + BeforeEach(func() { + responseBody, _ := json.Marshal(visibility) + handlerDetails = []HandlerDetails{ + {Method: http.MethodPost, Path: web.VisibilitiesURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusCreated}, + } + }) + It("should register successfully", func() { + responseVisibility, err := client.RegisterVisibility(visibility, params) + + Expect(err).ShouldNot(HaveOccurred()) + Expect(responseVisibility).To(Equal(visibility)) + }) + }) + + Context("When invalid visibility is returned by SM", func() { + BeforeEach(func() { + responseBody, _ := json.Marshal(struct { + ID bool `json:"id"` + }{ + ID: true, + }) + handlerDetails = []HandlerDetails{ + {Method: http.MethodPost, Path: web.VisibilitiesURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusCreated}, + } + }) + + It("should return error", func() { + responseVisibility, err := client.RegisterVisibility(visibility, params) + + Expect(err).Should(HaveOccurred()) + Expect(responseVisibility).To(BeNil()) + }) + }) + + Context("When invalid status code is returned by SM", func() { + Context("And status code is successful", func() { + BeforeEach(func() { + responseBody, _ := json.Marshal(visibility) + handlerDetails = []HandlerDetails{ + {Method: http.MethodPost, Path: web.VisibilitiesURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, + } + }) + It("should return error with status code", func() { + responseVisibility, err := client.RegisterVisibility(visibility, params) + + Expect(err).Should(HaveOccurred()) + verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + Expect(responseVisibility).To(BeNil()) + }) + }) + + Context("And status code is unsuccessful", func() { + BeforeEach(func() { + responseBody := []byte(`{ "description": "error"}`) + handlerDetails = []HandlerDetails{ + {Method: http.MethodPost, Path: web.VisibilitiesURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusBadRequest}, + } + }) + It("should return error with url and description", func() { + responseVisibility, err := client.RegisterVisibility(visibility, params) + + Expect(err).Should(HaveOccurred()) + verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + Expect(responseVisibility).To(BeNil()) + }) + }) + + Context("And response body is invalid", func() { + BeforeEach(func() { + responseBody := []byte(`{ "description": description", "error": "error"}`) + handlerDetails = []HandlerDetails{ + {Method: http.MethodPost, Path: web.VisibilitiesURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusBadRequest}, + } + }) + It("should return error without url and description if invalid response body", func() { + responseVisibility, err := client.RegisterVisibility(visibility, params) + + Expect(err).Should(HaveOccurred()) + verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + Expect(responseVisibility).To(BeNil()) + }) + }) + + }) + + Context("When invalid config is set", func() { + It("should return error", func() { + client = smclient.NewClient(fakeAuthClient, "invalidURL") + _, err := client.RegisterVisibility(visibility, params) + + Expect(err).Should(HaveOccurred()) + }) + }) + }) + + Describe("Update visibilities", func() { + Context("when an existing visibility is being updated", func() { + BeforeEach(func() { + responseBody, _ := json.Marshal(visibility) + + handlerDetails = []HandlerDetails{ + {Method: http.MethodPatch, Path: web.VisibilitiesURL + "/", ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, + } + }) + It("should be successfully removed", func() { + updatedVisibility, err := client.UpdateVisibility("id", visibility, params) + Expect(err).ShouldNot(HaveOccurred()) + Expect(updatedVisibility).To(Equal(visibility)) + }) + }) + + Context("when a non-existing visibility is being updated", func() { + BeforeEach(func() { + responseBody := []byte(`{"description": "Visibility not found"}`) + + handlerDetails = []HandlerDetails{ + {Method: http.MethodPatch, Path: web.VisibilitiesURL + "/", ResponseBody: responseBody, ResponseStatusCode: http.StatusNotFound}, + } + }) + It("should handle error", func() { + _, err := client.UpdateVisibility("id", visibility, params) + Expect(err).Should(HaveOccurred()) + verifyErrorMsg(err.Error(), handlerDetails[0].Path+"id", handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + }) + }) + + Context("when service manager returns a non-expected status code", func() { + BeforeEach(func() { + responseBody := []byte("{}") + + handlerDetails = []HandlerDetails{ + {Method: http.MethodPatch, Path: web.VisibilitiesURL + "/", ResponseBody: responseBody, ResponseStatusCode: http.StatusCreated}, + } + }) + It("should handle error", func() { + _, err := client.UpdateVisibility("id", visibility, params) + Expect(err).Should(HaveOccurred()) + verifyErrorMsg(err.Error(), handlerDetails[0].Path+"id", handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + + }) + }) + }) + + Describe("Delete visibility", func() { + Context("when an existing visibility is being deleted", func() { + BeforeEach(func() { + responseBody := []byte("{}") + + handlerDetails = []HandlerDetails{ + {Method: http.MethodDelete, Path: web.VisibilitiesURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusOK}, + } + }) + It("should be successfully removed", func() { + params.FieldQuery = append(params.FieldQuery, "id eq 'id'") + err := client.DeleteVisibilities(params) + Expect(err).ShouldNot(HaveOccurred()) + }) + }) + + Context("when service manager returns a non-expected status code", func() { + BeforeEach(func() { + responseBody := []byte("{}") + + handlerDetails = []HandlerDetails{ + {Method: http.MethodDelete, Path: web.VisibilitiesURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusCreated}, + } + }) + It("should handle error", func() { + params.FieldQuery = append(params.FieldQuery, "id eq 'id'") + err := client.DeleteVisibilities(params) + Expect(err).Should(HaveOccurred()) + verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + }) + }) + + Context("when service manager returns a status code not found", func() { + BeforeEach(func() { + responseBody := []byte(`{ "description": "Visibility not found" }`) + + handlerDetails = []HandlerDetails{ + {Method: http.MethodDelete, Path: web.VisibilitiesURL, ResponseBody: responseBody, ResponseStatusCode: http.StatusNotFound}, + } + }) + It("should handle error", func() { + params.FieldQuery = append(params.FieldQuery, "id eq 'id'") + err := client.DeleteVisibilities(params) + Expect(err).Should(HaveOccurred()) + verifyErrorMsg(err.Error(), handlerDetails[0].Path, handlerDetails[0].ResponseBody, handlerDetails[0].ResponseStatusCode) + }) + }) + }) +}) diff --git a/pkg/types/broker.go b/pkg/types/broker.go index a5c0ba13..acac6111 100644 --- a/pkg/types/broker.go +++ b/pkg/types/broker.go @@ -18,8 +18,8 @@ package types import ( "fmt" - "github.com/Peripli/service-manager/pkg/types" + "strconv" ) // Broker defines the data of a service broker. @@ -32,6 +32,7 @@ type Broker struct { Updated string `json:"updated_at,omitempty" yaml:"updated_at,omitempty"` Credentials *Credentials `json:"credentials,omitempty" yaml:"credentials,omitempty"` Labels types.Labels `json:"labels,omitempty" yaml:"labels,omitempty"` + Ready bool `json:"ready,omitempty" yaml:"ready,omitempty"` LastOperation *types.Operation `json:"last_operation,omitempty" yaml:"last_operation,omitempty"` } @@ -48,12 +49,12 @@ func (b *Broker) IsEmpty() bool { // TableData returns the data to populate a table func (b *Broker) TableData() *TableData { - result := &TableData{} + result := &TableData{Vertical: true} result.Headers = []string{"ID", "Name", "URL", "Description", "Created", "Updated", "Labels", "Last Op"} lastState := "-" if b.LastOperation != nil { - lastState = string(b.LastOperation.State) + lastState = formatLastOp(b.LastOperation) } row := []string{b.ID, b.Name, b.URL, b.Description, b.Created, b.Updated, formatLabels(b.Labels), lastState} result.Data = append(result.Data, row) @@ -89,10 +90,10 @@ func (b *Brokers) Message() string { // TableData returns the data to populate a table func (b *Brokers) TableData() *TableData { result := &TableData{} - result.Headers = []string{"ID", "Name", "URL", "Description", "Created", "Updated", "Labels"} + result.Headers = []string{"ID", "Name", "URL", "Description", "Created", "Updated", "Ready", "Labels"} for _, broker := range b.Brokers { - row := []string{broker.ID, broker.Name, broker.URL, broker.Description, broker.Created, broker.Updated, formatLabels(broker.Labels)} + row := []string{broker.ID, broker.Name, broker.URL, broker.Description, broker.Created, broker.Updated, strconv.FormatBool(broker.Ready), formatLabels(broker.Labels)} result.Data = append(result.Data, row) } diff --git a/pkg/types/label_changes.go b/pkg/types/label_changes.go index a62d25cb..9dd75109 100644 --- a/pkg/types/label_changes.go +++ b/pkg/types/label_changes.go @@ -16,9 +16,9 @@ package types -import "github.com/Peripli/service-manager/pkg/query" +import smtypes "github.com/Peripli/service-manager/pkg/types" // LabelChanges wraps multiple labels change request body structure type LabelChanges struct { - LabelChanges []*query.LabelChange `json:"labels,omitempty"` + LabelChanges []*smtypes.LabelChange `json:"labels,omitempty"` } diff --git a/pkg/types/operation.go b/pkg/types/operation.go new file mode 100644 index 00000000..4a0ecc10 --- /dev/null +++ b/pkg/types/operation.go @@ -0,0 +1,61 @@ +/* + * Copyright 2018 The Service Manager Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package types + +import ( + "encoding/json" + "github.com/Peripli/service-manager/pkg/types" +) + +// Operation defines the data of a operation. +type Operation struct { + ID string `json:"id,omitempty" yaml:"id,omitempty"` + Description string `json:"description,omitempty" yaml:"description,omitempty"` + Type string `json:"type,omitempty" yaml:"type,omitempty"` + State string `json:"state,omitempty" yaml:"state,omitempty"` + ResourceID string `json:"resource_id,omitempty" yaml:"resource_id,omitempty"` + ResourceType string `json:"resource_type,omitempty" yaml:"resource_type,omitempty"` + Errors json.RawMessage `json:"errors,omitempty" yaml:"errors,omitempty"` + Created string `json:"created_at,omitempty" yaml:"created_at,omitempty"` + Updated string `json:"updated_at,omitempty" yaml:"updated_at,omitempty"` + Labels types.Labels `json:"labels,omitempty" yaml:"labels,omitempty"` +} + +// Message title of the table +func (o *Operation) Message() string { + return "" +} + +// IsEmpty whether the structure is empty +func (o *Operation) IsEmpty() bool { + return false +} + +// TableData returns the data to populate a table +func (o *Operation) TableData() *TableData { + result := &TableData{Vertical: true} + var row []string + if o.State == string(types.FAILED) { + result.Headers = []string{"ID", "Type", "State", "Error"} + row = []string{o.ID, o.Type, o.State, string(o.Errors)} + } else { + result.Headers = []string{"ID", "Type", "State"} + row = []string{o.ID, o.Type, o.State} + } + result.Data = append(result.Data, row) + return result +} diff --git a/pkg/types/platform.go b/pkg/types/platform.go index e58eb567..88be9aa3 100644 --- a/pkg/types/platform.go +++ b/pkg/types/platform.go @@ -19,6 +19,7 @@ package types import ( "fmt" "github.com/Peripli/service-manager/pkg/types" + "strconv" ) // Platform defines the data of a platform. @@ -31,6 +32,7 @@ type Platform struct { Updated string `json:"updated_at,omitempty" yaml:"updated_at,omitempty"` Credentials *Credentials `json:"credentials,omitempty" yaml:"credentials,omitempty"` Labels types.Labels `json:"labels,omitempty" yaml:"labels,omitempty"` + Ready bool `json:"ready,omitempty" yaml:"ready,omitempty"` } // Message title of the table @@ -88,10 +90,10 @@ func (p *Platforms) Message() string { // TableData returns the data to populate a table func (p *Platforms) TableData() *TableData { result := &TableData{} - result.Headers = []string{"ID", "Name", "Type", "Description", "Created", "Updated", "Labels"} + result.Headers = []string{"ID", "Name", "Type", "Description", "Created", "Updated", "Ready", "Labels"} for _, platform := range p.Platforms { - row := []string{platform.ID, platform.Name, platform.Type, platform.Description, platform.Created, platform.Updated, formatLabels(platform.Labels)} + row := []string{platform.ID, platform.Name, platform.Type, platform.Description, platform.Created, platform.Updated, strconv.FormatBool(platform.Ready), formatLabels(platform.Labels)} result.Data = append(result.Data, row) } diff --git a/pkg/types/service_binding.go b/pkg/types/service_binding.go index a8b2b62b..ccf4bab6 100644 --- a/pkg/types/service_binding.go +++ b/pkg/types/service_binding.go @@ -33,16 +33,19 @@ type ServiceBinding struct { Labels types.Labels `json:"labels,omitempty" yaml:"labels,omitempty"` PagingSequence int64 `json:"-" yaml:"-"` - Credentials json.RawMessage `json:"credentials,omitempty" yaml:"credentials,omitempty"` - ServiceInstanceID string `json:"service_instance_id" yaml:"service_instance_id,omitempty"` - SyslogDrainURL string `json:"syslog_drain_url,omitempty" yaml:"syslog_drain_url,omitempty"` - RouteServiceURL string `json:"route_service_url,omitempty"` - VolumeMounts json.RawMessage `json:"-" yaml:"-"` - Endpoints json.RawMessage `json:"-" yaml:"-"` - Context json.RawMessage `json:"-" yaml:"-"` - BindResource json.RawMessage `json:"-" yaml:"-"` + Credentials json.RawMessage `json:"credentials,omitempty" yaml:"credentials,omitempty"` - Ready bool `json:"ready" yaml:"ready"` + ServiceInstanceID string `json:"service_instance_id" yaml:"service_instance_id,omitempty"` + ServiceInstanceName string `json:"service_instance_name,omitempty" yaml:"service_instance_name,omitempty"` + + SyslogDrainURL string `json:"syslog_drain_url,omitempty" yaml:"syslog_drain_url,omitempty"` + RouteServiceURL string `json:"route_service_url,omitempty"` + VolumeMounts json.RawMessage `json:"-" yaml:"-"` + Endpoints json.RawMessage `json:"-" yaml:"-"` + Context json.RawMessage `json:"context,omitempty" yaml:"context,omitempty"` + BindResource json.RawMessage `json:"-" yaml:"-"` + + Ready bool `json:"ready,omitempty" yaml:"ready,omitempty"` LastOperation *types.Operation `json:"last_operation,omitempty" yaml:"last_operation,omitempty"` } @@ -59,14 +62,14 @@ func (sb *ServiceBinding) IsEmpty() bool { // TableData returns the data to populate a table func (sb *ServiceBinding) TableData() *TableData { - result := &TableData{} - result.Headers = []string{"ID", "Name", "Service Instance ID", "Credentials", "Created", "Updated", "Ready", "Labels", "Last Op"} + result := &TableData{Vertical: true} + result.Headers = []string{"ID", "Name", "Service Instance Name", "Service Instance ID", "Credentials", "Created", "Updated", "Ready", "Labels", "Last Op"} lastState := "-" if sb.LastOperation != nil { - lastState = string(sb.LastOperation.State) + lastState = formatLastOp(sb.LastOperation) } - row := []string{sb.ID, sb.Name, sb.ServiceInstanceID, string(sb.Credentials), sb.CreatedAt, sb.UpdatedAt, strconv.FormatBool(sb.Ready), formatLabels(sb.Labels), lastState} + row := []string{sb.ID, sb.Name, sb.ServiceInstanceName, sb.ServiceInstanceID, string(sb.Credentials), sb.CreatedAt, sb.UpdatedAt, strconv.FormatBool(sb.Ready), formatLabels(sb.Labels), lastState} result.Data = append(result.Data, row) return result @@ -75,6 +78,7 @@ func (sb *ServiceBinding) TableData() *TableData { // ServiceBindings wraps an array of service bindings type ServiceBindings struct { ServiceBindings []ServiceBinding `json:"items" yaml:"items"` + Vertical bool `json:"-" yaml:"-"` } // Message title of the table @@ -99,17 +103,17 @@ func (sb *ServiceBindings) IsEmpty() bool { // TableData returns the data to populate a table func (sb *ServiceBindings) TableData() *TableData { - result := &TableData{} - result.Headers = []string{"ID", "Name", "Service Instance ID", "Credentials", "Created", "Updated", "Ready", "Usable", "Labels"} + result := &TableData{Vertical: sb.Vertical} + result.Headers = []string{"ID", "Name", "Instance Name", "Credentials", "Created", "Updated", "Ready", "Labels"} addLastOpColumn := false for _, binding := range sb.ServiceBindings { lastState := "-" if binding.LastOperation != nil { - lastState = string(binding.LastOperation.State) + lastState = formatLastOp(binding.LastOperation) addLastOpColumn = true } - row := []string{binding.ID, binding.Name, binding.ServiceInstanceID, string(binding.Credentials), binding.CreatedAt, binding.UpdatedAt, strconv.FormatBool(binding.Ready), formatLabels(binding.Labels), lastState} + row := []string{binding.ID, binding.Name, binding.ServiceInstanceName, string(binding.Credentials), binding.CreatedAt, binding.UpdatedAt, strconv.FormatBool(binding.Ready), formatLabels(binding.Labels), lastState} result.Data = append(result.Data, row) } diff --git a/pkg/types/service_instance.go b/pkg/types/service_instance.go index 46cbd37f..16768565 100644 --- a/pkg/types/service_instance.go +++ b/pkg/types/service_instance.go @@ -31,13 +31,15 @@ type ServiceInstance struct { CreatedAt string `json:"created_at,omitempty" yaml:"created_at,omitempty"` UpdatedAt string `json:"updated_at,omitempty" yaml:"updated_at,omitempty"` - Labels types.Labels `json:"labels,omitempty" yaml:"labels,omitempty"` - PagingSequence int64 `json:"-" yaml:"-"` - ServicePlanID string `json:"service_plan_id,omitempty" yaml:"service_plan_id,omitempty"` - PlatformID string `json:"platform_id,omitempty" yaml:"platform_id,omitempty"` + Labels types.Labels `json:"labels,omitempty" yaml:"labels,omitempty"` + ServiceID string `json:"service_id,omitempty" yaml:"service_id,omitempty"` + ServicePlanID string `json:"service_plan_id,omitempty" yaml:"service_plan_id,omitempty"` + PlatformID string `json:"platform_id,omitempty" yaml:"platform_id,omitempty"` + + Parameters json.RawMessage `json:"parameters,omitempty" yaml:"parameters,omitempty"` MaintenanceInfo json.RawMessage `json:"maintenance_info,omitempty" yaml:"-"` - Context json.RawMessage `json:"-" yaml:"-"` + Context json.RawMessage `json:"context,omitempty" yaml:"context,omitempty"` PreviousValues json.RawMessage `json:"-" yaml:"-"` Ready bool `json:"ready" yaml:"ready"` @@ -58,12 +60,12 @@ func (si *ServiceInstance) IsEmpty() bool { // TableData returns the data to populate a table func (si *ServiceInstance) TableData() *TableData { - result := &TableData{} + result := &TableData{Vertical: true} result.Headers = []string{"ID", "Name", "Service Plan ID", "Platform ID", "Created", "Updated", "Ready", "Usable", "Labels", "Last Op"} lastState := "-" if si.LastOperation != nil { - lastState = string(si.LastOperation.State) + lastState = formatLastOp(si.LastOperation) } row := []string{si.ID, si.Name, si.ServicePlanID, si.PlatformID, si.CreatedAt, si.UpdatedAt, strconv.FormatBool(si.Ready), strconv.FormatBool(si.Usable), formatLabels(si.Labels), lastState} result.Data = append(result.Data, row) @@ -74,6 +76,7 @@ func (si *ServiceInstance) TableData() *TableData { // ServiceInstances wraps an array of service instances type ServiceInstances struct { ServiceInstances []ServiceInstance `json:"items" yaml:"items"` + Vertical bool `json:"-" yaml:"-"` } // Message title of the table @@ -98,14 +101,14 @@ func (si *ServiceInstances) IsEmpty() bool { // TableData returns the data to populate a table func (si *ServiceInstances) TableData() *TableData { - result := &TableData{} + result := &TableData{Vertical: si.Vertical} result.Headers = []string{"ID", "Name", "Service Plan ID", "Platform ID", "Created", "Updated", "Ready", "Usable", "Labels"} addLastOpColumn := false for _, instance := range si.ServiceInstances { lastState := "-" if instance.LastOperation != nil { - lastState = string(instance.LastOperation.State) + lastState = formatLastOp(instance.LastOperation) addLastOpColumn = true } row := []string{instance.ID, instance.Name, instance.ServicePlanID, instance.PlatformID, instance.CreatedAt, instance.UpdatedAt, strconv.FormatBool(instance.Ready), strconv.FormatBool(instance.Usable), formatLabels(instance.Labels), lastState} diff --git a/pkg/types/service_offering.go b/pkg/types/service_offering.go index 30ffddf0..3caadb71 100644 --- a/pkg/types/service_offering.go +++ b/pkg/types/service_offering.go @@ -20,6 +20,7 @@ import ( "encoding/json" "fmt" "github.com/Peripli/service-manager/pkg/types" + "strconv" "strings" ) @@ -46,6 +47,7 @@ type ServiceOffering struct { BrokerName string `json:"broker_name,omitempty" yaml:"broker_name,omitempty"` Plans []ServicePlan `json:"plans,omitempty" yaml:"plans,omitempty"` Labels types.Labels `json:"labels,omitempty" yaml:"labels,omitempty"` + Ready bool `json:"ready,omitempty" yaml:"ready,omitempty"` } // Message title of the table @@ -145,10 +147,10 @@ func (so *ServiceOfferings) IsEmpty() bool { // TableData returns the data to populate a table func (so *ServiceOfferings) TableData() *TableData { result := &TableData{} - result.Headers = []string{"ID", "Name", "Description", "Broker ID", "Labels"} + result.Headers = []string{"ID", "Name", "Description", "Broker ID", "Ready", "Labels"} for _, v := range so.ServiceOfferings { - row := []string{v.ID, v.Name, v.Description, v.BrokerID, formatLabels(v.Labels)} + row := []string{v.ID, v.Name, v.Description, v.BrokerID, strconv.FormatBool(v.Ready), formatLabels(v.Labels)} result.Data = append(result.Data, row) } diff --git a/pkg/types/service_plan.go b/pkg/types/service_plan.go index ebf183bf..363de00e 100644 --- a/pkg/types/service_plan.go +++ b/pkg/types/service_plan.go @@ -20,6 +20,7 @@ import ( "encoding/json" "fmt" "github.com/Peripli/service-manager/pkg/types" + "strconv" ) // ServicePlan defines the data of a service plan. @@ -41,6 +42,7 @@ type ServicePlan struct { ServiceOfferingID string `json:"service_offering_id,omitempty" yaml:"service_offering_id,omitempty"` Labels types.Labels `json:"labels,omitempty" yaml:"labels,omitempty"` + Ready bool `json:"ready,omitempty" yaml:"ready,omitempty"` } // Message title of the table @@ -130,10 +132,10 @@ func (sp *ServicePlans) IsEmpty() bool { // TableData returns the data to populate a table func (sp *ServicePlans) TableData() *TableData { result := &TableData{} - result.Headers = []string{"ID", "Name", "Description", "Offering ID"} + result.Headers = []string{"ID", "Name", "Description", "Offering ID", "Ready"} for _, v := range sp.ServicePlans { - row := []string{v.ID, v.Name, v.Description, v.ServiceOfferingID} + row := []string{v.ID, v.Name, v.Description, v.ServiceOfferingID, strconv.FormatBool(v.Ready)} result.Data = append(result.Data, row) } diff --git a/pkg/types/tableData.go b/pkg/types/tableData.go index d26dfb7e..f31682e6 100644 --- a/pkg/types/tableData.go +++ b/pkg/types/tableData.go @@ -16,14 +16,28 @@ package types +import ( + "fmt" + "github.com/Peripli/service-manager/pkg/types" + "strings" +) + // TableData holds data for table header and content type TableData struct { - Headers []string - Data [][]string + Headers []string + Data [][]string + Vertical bool } // String implements Stringer interface func (table *TableData) String() string { + if table.Vertical { + return table.verticalTable() + } + return table.horizontalTable() +} + +func (table *TableData) horizontalTable() string { output := "" if len(table.Data) == 0 { return output @@ -52,14 +66,44 @@ func (table *TableData) String() string { return output } -func (table *TableData) fieldsLen() []int { +func (table *TableData) verticalTable() string { + output := "" + if len(table.Data) == 0 { + return output + } + + headerLen := table.headerLen() + maxHeaderLen := max(headerLen...) + + dataLen := table.dataLen() + maxDataLen := max(dataLen...) + + for i, header := range table.Headers { + output += "| " + output += pad(header, maxHeaderLen) + output += "| " + for _, row := range table.Data { + output += pad(row[i], maxDataLen) + output += "| " + } + output += "\n" + } + + return output +} + +func (table *TableData) headerLen() []int { fieldLen := make([]int, len(table.Headers)) for i, header := range table.Headers { if fieldLen[i] < len(header)+2 { fieldLen[i] = len(header) + 2 } } + return fieldLen +} +func (table *TableData) dataLen() []int { + fieldLen := make([]int, len(table.Headers)) for _, row := range table.Data { for i, cell := range row { if fieldLen[i] < len(cell)+2 { @@ -71,6 +115,17 @@ func (table *TableData) fieldsLen() []int { return fieldLen } +func (table *TableData) fieldsLen() []int { + fieldLen := make([]int, len(table.Headers)) + headerLen := table.headerLen() + dataLen := table.dataLen() + + for i := range fieldLen { + fieldLen[i] = max(headerLen[i], dataLen[i]) + } + return fieldLen +} + func pad(s string, p int) string { result := s @@ -90,3 +145,28 @@ func line(p int) string { return result } + +func max(arr ...int) int { + tmp := arr[0] + for _, i := range arr { + if i > tmp { + tmp = i + } + } + return tmp +} + +func formatLabels(labels types.Labels) string { + formattedLabels := make([]string, 0, len(labels)) + for i, v := range labels { + formattedLabels = append(formattedLabels, i+"="+strings.Join(v, ",")) + } + return strings.Join(formattedLabels, " ") +} + +func formatLastOp(operation *types.Operation) string { + if operation.State != types.FAILED { + return fmt.Sprintf("%s %s", operation.Type, operation.State) + } + return fmt.Sprintf("%s %s %s", operation.Type, operation.State, operation.Errors) +} diff --git a/pkg/types/visibility.go b/pkg/types/visibility.go index 6a65017e..0271afcd 100644 --- a/pkg/types/visibility.go +++ b/pkg/types/visibility.go @@ -19,7 +19,7 @@ package types import ( "fmt" "github.com/Peripli/service-manager/pkg/types" - "strings" + "strconv" ) // Visibility defines the data of a visibility @@ -30,6 +30,7 @@ type Visibility struct { CreatedAt string `json:"created_at,omitempty" yaml:"created_at,omitempty"` UpdatedAt string `json:"updated_at,omitempty" yaml:"updated_at,omitempty"` Labels types.Labels `json:"labels,omitempty" yaml:"labels,omitempty"` + Ready bool `json:"ready,omitempty" yaml:"ready,omitempty"` } // Message title of the table @@ -81,20 +82,12 @@ func (v *Visibilities) Message() string { // TableData returns the data to populate a table func (v *Visibilities) TableData() *TableData { result := &TableData{} - result.Headers = []string{"ID", "Platform ID", "Service Plan ID", "Labels"} + result.Headers = []string{"ID", "Platform ID", "Service Plan ID", "Ready", "Labels"} for _, visibility := range v.Visibilities { - row := []string{visibility.ID, visibility.PlatformID, visibility.ServicePlanID, formatLabels(visibility.Labels)} + row := []string{visibility.ID, visibility.PlatformID, visibility.ServicePlanID, strconv.FormatBool(visibility.Ready), formatLabels(visibility.Labels)} result.Data = append(result.Data, row) } return result } - -func formatLabels(labels types.Labels) string { - formattedLabels := make([]string, 0, len(labels)) - for i, v := range labels { - formattedLabels = append(formattedLabels, i+"="+strings.Join(v, ",")) - } - return strings.Join(formattedLabels, " ") -}