Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 34 additions & 15 deletions actor/v7action/service_app_binding.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,16 @@ type CreateServiceAppBindingParams struct {
Parameters types.OptionalObject
}

type DeleteServiceAppBindingParams struct {
type ListServiceAppBindingParams struct {
SpaceGUID string
ServiceInstanceName string
AppName string
}

type DeleteServiceAppBindingParams struct {
ServiceBindingGUID string
}

func (actor Actor) CreateServiceAppBinding(params CreateServiceAppBindingParams) (chan PollJobEvent, Warnings, error) {
var (
serviceInstance resources.ServiceInstance
Expand Down Expand Up @@ -60,13 +64,11 @@ func (actor Actor) CreateServiceAppBinding(params CreateServiceAppBindingParams)
}
}

func (actor Actor) DeleteServiceAppBinding(params DeleteServiceAppBindingParams) (chan PollJobEvent, Warnings, error) {
func (actor Actor) ListServiceAppBindings(params ListServiceAppBindingParams) ([]resources.ServiceCredentialBinding, Warnings, error) {
var (
serviceInstance resources.ServiceInstance
app resources.Application
binding resources.ServiceCredentialBinding
jobURL ccv3.JobURL
stream chan PollJobEvent
bindings []resources.ServiceCredentialBinding
)

warnings, err := railway.Sequentially(
Expand All @@ -79,11 +81,30 @@ func (actor Actor) DeleteServiceAppBinding(params DeleteServiceAppBindingParams)
return
},
func() (warnings ccv3.Warnings, err error) {
binding, warnings, err = actor.getServiceAppBinding(serviceInstance.GUID, app.GUID)
bindings, warnings, err = actor.getServiceAppBindings(serviceInstance.GUID, app.GUID)
return
},
)

switch err.(type) {
case nil:
return bindings, Warnings(warnings), nil
case ccerror.ApplicationNotFoundError:
return nil, Warnings(warnings), actionerror.ApplicationNotFoundError{Name: params.AppName}
default:
return nil, Warnings(warnings), err
}
}

func (actor Actor) DeleteServiceAppBinding(params DeleteServiceAppBindingParams) (chan PollJobEvent, Warnings, error) {
var (
jobURL ccv3.JobURL
stream chan PollJobEvent
)

warnings, err := railway.Sequentially(
func() (warnings ccv3.Warnings, err error) {
jobURL, warnings, err = actor.CloudControllerClient.DeleteServiceCredentialBinding(binding.GUID)
jobURL, warnings, err = actor.CloudControllerClient.DeleteServiceCredentialBinding(params.ServiceBindingGUID)
return
},
func() (warnings ccv3.Warnings, err error) {
Expand All @@ -94,10 +115,10 @@ func (actor Actor) DeleteServiceAppBinding(params DeleteServiceAppBindingParams)

switch err.(type) {
case nil:

return stream, Warnings(warnings), nil
case ccerror.ApplicationNotFoundError:
return nil, Warnings(warnings), actionerror.ApplicationNotFoundError{Name: params.AppName}
default:

return nil, Warnings(warnings), err
}
}
Expand All @@ -120,24 +141,22 @@ func (actor Actor) createServiceAppBinding(serviceInstanceGUID, appGUID, binding
}
}

func (actor Actor) getServiceAppBinding(serviceInstanceGUID, appGUID string) (resources.ServiceCredentialBinding, ccv3.Warnings, error) {
func (actor Actor) getServiceAppBindings(serviceInstanceGUID, appGUID string) ([]resources.ServiceCredentialBinding, ccv3.Warnings, error) {
bindings, warnings, err := actor.CloudControllerClient.GetServiceCredentialBindings(
ccv3.Query{Key: ccv3.TypeFilter, Values: []string{"app"}},
ccv3.Query{Key: ccv3.ServiceInstanceGUIDFilter, Values: []string{serviceInstanceGUID}},
ccv3.Query{Key: ccv3.AppGUIDFilter, Values: []string{appGUID}},
ccv3.Query{Key: ccv3.PerPage, Values: []string{"1"}},
ccv3.Query{Key: ccv3.Page, Values: []string{"1"}},
)

switch {
case err != nil:
return resources.ServiceCredentialBinding{}, warnings, err
return []resources.ServiceCredentialBinding{}, warnings, err
case len(bindings) == 0:
return resources.ServiceCredentialBinding{}, warnings, actionerror.ServiceBindingNotFoundError{
return []resources.ServiceCredentialBinding{}, warnings, actionerror.ServiceBindingNotFoundError{
AppGUID: appGUID,
ServiceInstanceGUID: serviceInstanceGUID,
}
default:
return bindings[0], warnings, nil
return bindings, warnings, nil
}
}
108 changes: 73 additions & 35 deletions actor/v7action/service_app_binding_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -248,22 +248,21 @@ var _ = Describe("Service App Binding Action", func() {
})
})

Describe("DeleteServiceAppBinding", func() {
Describe("ListServiceAppBindings", func() {
const (
serviceInstanceName = "fake-service-instance-name"
serviceInstanceGUID = "fake-service-instance-guid"
appName = "fake-app-name"
appGUID = "fake-app-guid"
spaceGUID = "fake-space-guid"
bindingGUID = "fake-binding-guid"
fakeJobURL = ccv3.JobURL("fake-job-url")
)

var (
params DeleteServiceAppBindingParams
warnings Warnings
executionError error
stream chan PollJobEvent
params ListServiceAppBindingParams
warnings Warnings
executionError error
serviceCredentialBindings []resources.ServiceCredentialBinding
)

BeforeEach(func() {
Expand Down Expand Up @@ -294,47 +293,27 @@ var _ = Describe("Service App Binding Action", func() {
nil,
)

fakeCloudControllerClient.DeleteServiceCredentialBindingReturns(
fakeJobURL,
ccv3.Warnings{"delete binding warning"},
nil,
)

fakeStream := make(chan ccv3.PollJobEvent)
fakeCloudControllerClient.PollJobToEventStreamReturns(fakeStream)
go func() {
fakeStream <- ccv3.PollJobEvent{
State: constant.JobPolling,
Warnings: ccv3.Warnings{"poll warning"},
}
}()

params = DeleteServiceAppBindingParams{
SpaceGUID: spaceGUID,
ServiceInstanceName: serviceInstanceName,
AppName: appName,
params = ListServiceAppBindingParams{
SpaceGUID: "fake-space-guid",
ServiceInstanceName: "fake-service-instance-name",
AppName: "fake-app-name",
}
})

JustBeforeEach(func() {
stream, warnings, executionError = actor.DeleteServiceAppBinding(params)
serviceCredentialBindings, warnings, executionError = actor.ListServiceAppBindings(params)
})

It("returns an event stream, warnings, and no errors", func() {
It("returns an event stream, warning, and no errors", func() {
Expect(executionError).NotTo(HaveOccurred())

Expect(warnings).To(ConsistOf(Warnings{
"get instance warning",
"get app warning",
"get bindings warning",
"delete binding warning",
}))

Eventually(stream).Should(Receive(Equal(PollJobEvent{
State: JobPolling,
Warnings: Warnings{"poll warning"},
Err: nil,
})))
Expect(serviceCredentialBindings).To(Equal([]resources.ServiceCredentialBinding{{GUID: bindingGUID}}))
})

Describe("service instance lookup", func() {
Expand Down Expand Up @@ -425,8 +404,6 @@ var _ = Describe("Service App Binding Action", func() {
ccv3.Query{Key: ccv3.TypeFilter, Values: []string{"app"}},
ccv3.Query{Key: ccv3.AppGUIDFilter, Values: []string{appGUID}},
ccv3.Query{Key: ccv3.ServiceInstanceGUIDFilter, Values: []string{serviceInstanceGUID}},
ccv3.Query{Key: ccv3.PerPage, Values: []string{"1"}},
ccv3.Query{Key: ccv3.Page, Values: []string{"1"}},
))
})

Expand Down Expand Up @@ -463,6 +440,67 @@ var _ = Describe("Service App Binding Action", func() {
})
})
})
})

Describe("DeleteServiceAppBinding", func() {
const (
bindingGUID = "fake-binding-guid"
fakeJobURL = ccv3.JobURL("fake-job-url")
)

var (
params DeleteServiceAppBindingParams
warnings Warnings
executionError error
stream chan PollJobEvent
)

BeforeEach(func() {
fakeCloudControllerClient.GetServiceCredentialBindingsReturns(
[]resources.ServiceCredentialBinding{
{GUID: bindingGUID},
},
ccv3.Warnings{"get bindings warning"},
nil,
)

fakeCloudControllerClient.DeleteServiceCredentialBindingReturns(
fakeJobURL,
ccv3.Warnings{"delete binding warning"},
nil,
)

fakeStream := make(chan ccv3.PollJobEvent)
fakeCloudControllerClient.PollJobToEventStreamReturns(fakeStream)
go func() {
fakeStream <- ccv3.PollJobEvent{
State: constant.JobPolling,
Warnings: ccv3.Warnings{"poll warning"},
}
}()

params = DeleteServiceAppBindingParams{
ServiceBindingGUID: bindingGUID,
}
})

JustBeforeEach(func() {
stream, warnings, executionError = actor.DeleteServiceAppBinding(params)
})

It("returns an event stream, warning, and no errors", func() {
Expect(executionError).NotTo(HaveOccurred())

Expect(warnings).To(ConsistOf(Warnings{
"delete binding warning",
}))

Eventually(stream).Should(Receive(Equal(PollJobEvent{
State: JobPolling,
Warnings: Warnings{"poll warning"},
Err: nil,
})))
})

Describe("initiating the delete", func() {
It("makes the correct call", func() {
Expand Down
1 change: 1 addition & 0 deletions command/v7/actor.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ type Actor interface {
GetUAAAPIVersion() (string, error)
GetUnstagedNewestPackageGUID(appGuid string) (string, v7action.Warnings, error)
GetUser(username, origin string) (resources.User, error)
ListServiceAppBindings(params v7action.ListServiceAppBindingParams) ([]resources.ServiceCredentialBinding, v7action.Warnings, error)
MakeCurlRequest(httpMethod string, path string, customHeaders []string, httpData string, failOnHTTPError bool) ([]byte, *http.Response, error)
MapRoute(routeGUID string, appGUID string, destinationProtocol string) (v7action.Warnings, error)
Marketplace(filter v7action.MarketplaceFilter) ([]v7action.ServiceOfferingWithPlans, v7action.Warnings, error)
Expand Down
57 changes: 44 additions & 13 deletions command/v7/unbind_service_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ import (
"code.cloudfoundry.org/cli/v8/actor/v7action"
"code.cloudfoundry.org/cli/v8/command/flag"
"code.cloudfoundry.org/cli/v8/command/v7/shared"
"code.cloudfoundry.org/cli/v8/resources"
)

type UnbindServiceCommand struct {
BaseCommand

RequiredArgs flag.BindServiceArgs `positional-args:"yes"`
Wait bool `short:"w" long:"wait" description:"Wait for the operation to complete"`
Guid string `long:"guid" description:"Only delete the service binding with the specified GUID"`
relatedCommands interface{} `related_commands:"apps, delete-service, services"`
}

Expand All @@ -24,7 +26,7 @@ func (cmd UnbindServiceCommand) Execute(args []string) error {
return err
}

stream, warnings, err := cmd.Actor.DeleteServiceAppBinding(v7action.DeleteServiceAppBindingParams{
bindings, warnings, err := cmd.Actor.ListServiceAppBindings(v7action.ListServiceAppBindingParams{
SpaceGUID: cmd.Config.TargetedSpace().GUID,
ServiceInstanceName: cmd.RequiredArgs.ServiceInstanceName,
AppName: cmd.RequiredArgs.AppName,
Expand All @@ -33,25 +35,54 @@ func (cmd UnbindServiceCommand) Execute(args []string) error {
switch err.(type) {
case nil:
case actionerror.ServiceBindingNotFoundError:
cmd.UI.DisplayText("Binding between {{.ServiceInstanceName}} and {{.AppName}} did not exist", cmd.names())
cmd.UI.DisplayText("Binding between {{.ServiceInstanceName}} and {{.AppName}} does not exist", cmd.names())
cmd.UI.DisplayOK()
return nil
default:
return err
}

completed, err := shared.WaitForResult(stream, cmd.UI, cmd.Wait)
switch {
case err != nil:
return err
case completed:
cmd.UI.DisplayOK()
return nil
default:
cmd.UI.DisplayOK()
cmd.UI.DisplayText("Unbinding in progress. Use 'cf service {{.ServiceInstanceName}}' to check operation status.", cmd.names())
return nil
// If a GUID is provided, narrow down to the binding matching that GUID.
if cmd.Guid != "" {
var filtered []resources.ServiceCredentialBinding
for _, b := range bindings {
if b.GUID == cmd.Guid {
filtered = append(filtered, b)
break
}
}
if len(filtered) == 0 {
cmd.UI.DisplayText("Service binding with GUID {{.BindingGUID}} does not exist", map[string]interface{}{"BindingGUID": cmd.Guid})
cmd.UI.DisplayOK()
return nil
}
bindings = filtered
}

for _, binding := range bindings {
cmd.UI.DisplayText("Deleting service binding {{.BindingGUID}}...", map[string]interface{}{"BindingGUID": binding.GUID})
stream, warnings, err := cmd.Actor.DeleteServiceAppBinding(v7action.DeleteServiceAppBindingParams{
ServiceBindingGUID: binding.GUID,
})
cmd.UI.DisplayWarnings(warnings)
switch err.(type) {
case nil:
default:
return err
}

completed, err := shared.WaitForResult(stream, cmd.UI, cmd.Wait)
switch {
case err != nil:
return err
case completed:
cmd.UI.DisplayOK()
default:
cmd.UI.DisplayOK()
cmd.UI.DisplayText("Unbinding in progress. Use 'cf service {{.ServiceInstanceName}}' to check operation status.", cmd.names())
}
}
return nil
}

func (cmd UnbindServiceCommand) Usage() string {
Expand Down
Loading
Loading