Skip to content

Commit b9b9ba2

Browse files
committed
1 parent 8da618e commit b9b9ba2

File tree

7 files changed

+132
-9
lines changed

7 files changed

+132
-9
lines changed

actor/v7action/service_app_binding.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ type CreateServiceAppBindingParams struct {
1515
AppName string
1616
BindingName string
1717
Parameters types.OptionalObject
18+
Strategy resources.BindingStrategyType
1819
}
1920

2021
type DeleteServiceAppBindingParams struct {
@@ -41,7 +42,7 @@ func (actor Actor) CreateServiceAppBinding(params CreateServiceAppBindingParams)
4142
return
4243
},
4344
func() (warnings ccv3.Warnings, err error) {
44-
jobURL, warnings, err = actor.createServiceAppBinding(serviceInstance.GUID, app.GUID, params.BindingName, params.Parameters)
45+
jobURL, warnings, err = actor.createServiceAppBinding(serviceInstance.GUID, app.GUID, params.BindingName, params.Parameters, params.Strategy)
4546
return
4647
},
4748
func() (warnings ccv3.Warnings, err error) {
@@ -102,13 +103,14 @@ func (actor Actor) DeleteServiceAppBinding(params DeleteServiceAppBindingParams)
102103
}
103104
}
104105

105-
func (actor Actor) createServiceAppBinding(serviceInstanceGUID, appGUID, bindingName string, parameters types.OptionalObject) (ccv3.JobURL, ccv3.Warnings, error) {
106+
func (actor Actor) createServiceAppBinding(serviceInstanceGUID, appGUID, bindingName string, parameters types.OptionalObject, strategy resources.BindingStrategyType) (ccv3.JobURL, ccv3.Warnings, error) {
106107
jobURL, warnings, err := actor.CloudControllerClient.CreateServiceCredentialBinding(resources.ServiceCredentialBinding{
107108
Type: resources.AppBinding,
108109
Name: bindingName,
109110
ServiceInstanceGUID: serviceInstanceGUID,
110111
AppGUID: appGUID,
111112
Parameters: parameters,
113+
Strategy: strategy,
112114
})
113115
switch err.(type) {
114116
case nil:

actor/v7action/service_app_binding_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ var _ = Describe("Service App Binding Action", func() {
3535
bindingName = "fake-binding-name"
3636
spaceGUID = "fake-space-guid"
3737
fakeJobURL = ccv3.JobURL("fake-job-url")
38+
strategy = "single"
3839
)
3940

4041
var (
@@ -87,6 +88,7 @@ var _ = Describe("Service App Binding Action", func() {
8788
Parameters: types.NewOptionalObject(map[string]interface{}{
8889
"foo": "bar",
8990
}),
91+
Strategy: resources.SingleBindingStrategy,
9092
}
9193
})
9294

@@ -202,6 +204,7 @@ var _ = Describe("Service App Binding Action", func() {
202204
Parameters: types.NewOptionalObject(map[string]interface{}{
203205
"foo": "bar",
204206
}),
207+
Strategy: strategy,
205208
}))
206209
})
207210

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package flag
2+
3+
import (
4+
"strings"
5+
6+
"code.cloudfoundry.org/cli/v8/resources"
7+
flags "github.com/jessevdk/go-flags"
8+
)
9+
10+
type ServiceBindingStrategy struct {
11+
Strategy resources.BindingStrategyType
12+
}
13+
14+
func (ServiceBindingStrategy) Complete(prefix string) []flags.Completion {
15+
return completions([]string{"single", "multiple"}, prefix, false)
16+
}
17+
18+
func (h *ServiceBindingStrategy) UnmarshalFlag(val string) error {
19+
valLower := strings.ToLower(val)
20+
switch valLower {
21+
case "single", "multiple":
22+
h.Strategy = resources.BindingStrategyType(valLower)
23+
default:
24+
return &flags.Error{
25+
Type: flags.ErrRequired,
26+
Message: `STRATEGY must be "single" or "multiple"`,
27+
}
28+
}
29+
return nil
30+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package flag_test
2+
3+
import (
4+
. "code.cloudfoundry.org/cli/v8/command/flag"
5+
"code.cloudfoundry.org/cli/v8/resources"
6+
flags "github.com/jessevdk/go-flags"
7+
. "github.com/onsi/ginkgo/v2"
8+
. "github.com/onsi/gomega"
9+
)
10+
11+
var _ = Describe("ServiceBindingStrategy", func() {
12+
var sbs ServiceBindingStrategy
13+
14+
Describe("Complete", func() {
15+
DescribeTable("returns list of completions",
16+
func(prefix string, matches []flags.Completion) {
17+
completions := sbs.Complete(prefix)
18+
Expect(completions).To(Equal(matches))
19+
},
20+
Entry("returns 'single' when passed 's'", "s",
21+
[]flags.Completion{{Item: "single"}}),
22+
Entry("returns 'single' when passed 'S'", "S",
23+
[]flags.Completion{{Item: "single"}}),
24+
Entry("returns 'multiple' when passed 'm'", "m",
25+
[]flags.Completion{{Item: "multiple"}}),
26+
Entry("returns 'multiple' when passed 'M'", "M",
27+
[]flags.Completion{{Item: "multiple"}}),
28+
Entry("returns 'single' and 'multiple' when passed ''", "",
29+
[]flags.Completion{{Item: "single"}, {Item: "multiple"}}),
30+
)
31+
})
32+
33+
Describe("UnmarshalFlag", func() {
34+
BeforeEach(func() {
35+
sbs = ServiceBindingStrategy{}
36+
})
37+
38+
DescribeTable("downcases and sets strategy",
39+
func(input string, expected resources.BindingStrategyType) {
40+
err := sbs.UnmarshalFlag(input)
41+
Expect(err).ToNot(HaveOccurred())
42+
Expect(sbs.Strategy).To(Equal(expected))
43+
},
44+
Entry("sets 'single' when passed 'single'", "single", resources.SingleBindingStrategy),
45+
Entry("sets 'single' when passed 'sInGlE'", "sInGlE", resources.SingleBindingStrategy),
46+
Entry("sets 'multiple' when passed 'multiple'", "multiple", resources.MultipleBindingStrategy),
47+
Entry("sets 'multiple' when passed 'MuLtIpLe'", "MuLtIpLe", resources.MultipleBindingStrategy),
48+
)
49+
50+
When("passed anything else", func() {
51+
It("returns an error", func() {
52+
err := sbs.UnmarshalFlag("banana")
53+
Expect(err).To(MatchError(&flags.Error{
54+
Type: flags.ErrRequired,
55+
Message: `STRATEGY must be "single" or "multiple"`,
56+
}))
57+
Expect(sbs.Strategy).To(BeEmpty())
58+
})
59+
})
60+
})
61+
})

command/v7/bind_service_command.go

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,12 @@ import (
1111
type BindServiceCommand struct {
1212
BaseCommand
1313

14-
RequiredArgs flag.BindServiceArgs `positional-args:"yes"`
15-
BindingName flag.BindingName `long:"binding-name" description:"Name to expose service instance to app process with (Default: service instance name)"`
16-
ParametersAsJSON flag.JSONOrFileWithValidation `short:"c" description:"Valid JSON object containing service-specific configuration parameters, provided either in-line or in a file. For a list of supported configuration parameters, see documentation for the particular service offering."`
17-
Wait bool `short:"w" long:"wait" description:"Wait for the operation to complete"`
18-
relatedCommands interface{} `related_commands:"services"`
14+
RequiredArgs flag.BindServiceArgs `positional-args:"yes"`
15+
BindingName flag.BindingName `long:"binding-name" description:"Name to expose service instance to app process with (Default: service instance name)"`
16+
ParametersAsJSON flag.JSONOrFileWithValidation `short:"c" description:"Valid JSON object containing service-specific configuration parameters, provided either in-line or in a file. For a list of supported configuration parameters, see documentation for the particular service offering."`
17+
ServiceBindingStrategy flag.ServiceBindingStrategy `long:"strategy" description:"Service binding strategy. Valid values are 'single' (default) and 'multiple'."`
18+
Wait bool `short:"w" long:"wait" description:"Wait for the operation to complete"`
19+
relatedCommands interface{} `related_commands:"services"`
1920
}
2021

2122
func (cmd BindServiceCommand) Execute(args []string) error {
@@ -33,6 +34,7 @@ func (cmd BindServiceCommand) Execute(args []string) error {
3334
AppName: cmd.RequiredArgs.AppName,
3435
BindingName: cmd.BindingName.Value,
3536
Parameters: types.OptionalObject(cmd.ParametersAsJSON),
37+
Strategy: cmd.ServiceBindingStrategy.Strategy,
3638
})
3739
cmd.UI.DisplayWarnings(warnings)
3840

@@ -82,7 +84,12 @@ Example of valid JSON object:
8284
8385
Optionally provide a binding name for the association between an app and a service instance:
8486
85-
CF_NAME bind-service APP_NAME SERVICE_INSTANCE --binding-name BINDING_NAME`
87+
CF_NAME bind-service APP_NAME SERVICE_INSTANCE --binding-name BINDING_NAME
88+
89+
Optionally provide the binding strategy type. Valid options are 'single' (default) and 'multiple'. The 'multiple' strategy allows multiple bindings between the same app and service instance.
90+
This is useful for credential rotation scenarios.
91+
92+
CF_NAME bind-service APP_NAME SERVICE_INSTANCE --strategy multiple`
8693
}
8794

8895
func (cmd BindServiceCommand) Examples() string {

resources/service_credential_binding_resource.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@ const (
1212
KeyBinding ServiceCredentialBindingType = "key"
1313
)
1414

15+
type BindingStrategyType string
16+
17+
const (
18+
SingleBindingStrategy BindingStrategyType = "single"
19+
MultipleBindingStrategy BindingStrategyType = "multiple"
20+
)
21+
1522
type ServiceCredentialBinding struct {
1623
// Type is either "app" or "key"
1724
Type ServiceCredentialBindingType `jsonry:"type,omitempty"`
@@ -31,6 +38,8 @@ type ServiceCredentialBinding struct {
3138
LastOperation LastOperation `jsonry:"last_operation"`
3239
// Parameters can be specified when creating a binding
3340
Parameters types.OptionalObject `jsonry:"parameters"`
41+
// Strategy can be "single" (default) or "multiple"
42+
Strategy BindingStrategyType `jsonry:"strategy,omitempty"`
3443
}
3544

3645
func (s ServiceCredentialBinding) MarshalJSON() ([]byte, error) {

resources/service_credential_binding_resource_test.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,15 @@ var _ = Describe("service credential binding resource", func() {
6060
}
6161
}`,
6262
),
63+
Entry(
64+
"strategy",
65+
ServiceCredentialBinding{
66+
Strategy: SingleBindingStrategy,
67+
},
68+
`{
69+
"strategy": "single"
70+
}`,
71+
),
6372
Entry(
6473
"everything",
6574
ServiceCredentialBinding{
@@ -71,6 +80,7 @@ var _ = Describe("service credential binding resource", func() {
7180
Parameters: types.NewOptionalObject(map[string]interface{}{
7281
"foo": "bar",
7382
}),
83+
Strategy: MultipleBindingStrategy,
7484
},
7585
`{
7686
"type": "app",
@@ -90,7 +100,8 @@ var _ = Describe("service credential binding resource", func() {
90100
},
91101
"parameters": {
92102
"foo": "bar"
93-
}
103+
},
104+
"strategy": "multiple"
94105
}`,
95106
),
96107
)

0 commit comments

Comments
 (0)