Skip to content
This repository has been archived by the owner on May 6, 2022. It is now read-only.

WIP: Define default binding params on plans #2435

Closed
wants to merge 6 commits into from
Closed
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
17 changes: 17 additions & 0 deletions pkg/apis/servicecatalog/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,13 @@ type CommonServiceClassSpec struct {
// plan and then instance-defined parameters taking precedence over the class
// defaults.
DefaultProvisionParameters *runtime.RawExtension

// DefaultBindingParameters are default parameters passed to the broker
// when an instance of this class is binded. Any parameters defined on
// the plan and binding are merged with these defaults, with
// plan and then binding-defined parameters taking precedence over the class
// defaults.
DefaultBindingParameters *runtime.RawExtension
}

// ClusterServiceClassSpec represents the details about a ClusterServiceClass.
Expand Down Expand Up @@ -589,6 +596,12 @@ type CommonServicePlanSpec struct {
// the instance are merged with these defaults, with instance-defined
// parameters taking precedence over defaults.
DefaultProvisionParameters *runtime.RawExtension

// DefaultBindingParameters are default parameters passed to the broker
// when an instance of this plan is binded. Any parameters defined on
// the binding are merged with these defaults, with binding-defined
// parameters taking precedence over defaults.
DefaultBindingParameters *runtime.RawExtension
}

// ClusterServicePlanSpec represents details about the ClusterServicePlan
Expand Down Expand Up @@ -1148,6 +1161,10 @@ type ServiceBindingStatus struct {

// UnbindStatus describes what has been done to unbind a ServiceBinding
UnbindStatus ServiceBindingUnbindStatus

// DefaultBindingParameters are the default parameters applied to this
// binding.
DefaultBindingParameters *runtime.RawExtension
}

// ServiceBindingCondition condition information for a ServiceBinding.
Expand Down
6 changes: 6 additions & 0 deletions pkg/apis/servicecatalog/unstructured_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ func doUnstructuredRoundTrip(t *testing.T, group testapi.TestGroup, kind string)
},
func(is *servicecatalog.CommonServicePlanSpec, c fuzz.Continue) {
c.FuzzNoCustom(is)
is.DefaultBindingParameters = nil
is.DefaultProvisionParameters = nil
is.ExternalMetadata = nil
is.ServiceBindingCreateParameterSchema = nil
Expand All @@ -80,6 +81,7 @@ func doUnstructuredRoundTrip(t *testing.T, group testapi.TestGroup, kind string)
},
func(cs *servicecatalog.CommonServiceClassSpec, c fuzz.Continue) {
c.FuzzNoCustom(cs)
cs.DefaultBindingParameters = nil
cs.DefaultProvisionParameters = nil
cs.ExternalMetadata = nil
},
Expand All @@ -95,6 +97,10 @@ func doUnstructuredRoundTrip(t *testing.T, group testapi.TestGroup, kind string)
}
bs.Parameters = nil
},
func(bs *servicecatalog.ServiceBindingStatus, c fuzz.Continue) {
c.FuzzNoCustom(bs)
bs.DefaultBindingParameters = nil
},
func(ps *servicecatalog.ServiceInstancePropertiesState, c fuzz.Continue) {
c.FuzzNoCustom(ps)
ps.Parameters = nil
Expand Down
17 changes: 17 additions & 0 deletions pkg/apis/servicecatalog/v1beta1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,13 @@ type CommonServiceClassSpec struct {
// plan and then instance-defined parameters taking precedence over the class
// defaults.
DefaultProvisionParameters *runtime.RawExtension `json:"defaultProvisionParameters,omitempty"`

// DefaultBindingParameters are default parameters passed to the broker
// when an instance of this class is binded. Any parameters defined on
// the plan and binding are merged with these defaults, with
// plan and then binding-defined parameters taking precedence over the class
// defaults.
DefaultBindingParameters *runtime.RawExtension `json:"defaultBindingParameters,omitempty"`
}

// ClusterServiceClassSpec represents the details about a ClusterServiceClass
Expand Down Expand Up @@ -647,6 +654,12 @@ type CommonServicePlanSpec struct {
// the instance are merged with these defaults, with instance-defined
// parameters taking precedence over defaults.
DefaultProvisionParameters *runtime.RawExtension `json:"defaultProvisionParameters,omitempty"`

// DefaultBindingParameters are default parameters passed to the broker
// when an instance of this plan is binded. Any parameters defined on
// the binding are merged with these defaults, with binding-defined
// parameters taking precedence over defaults.
DefaultBindingParameters *runtime.RawExtension `json:"defaultBindingParameters,omitempty"`
}

// ClusterServicePlanSpec represents details about a ClusterServicePlan.
Expand Down Expand Up @@ -1248,6 +1261,10 @@ type ServiceBindingStatus struct {

// UnbindStatus describes what has been done to unbind the ServiceBinding.
UnbindStatus ServiceBindingUnbindStatus `json:"unbindStatus"`

// DefaultBindingParameters are the default parameters applied to this
// binding.
DefaultBindingParameters *runtime.RawExtension `json:"defaultBindingParameters,omitempty"`
}

// ServiceBindingCondition condition information for a ServiceBinding.
Expand Down
6 changes: 6 additions & 0 deletions pkg/apis/servicecatalog/v1beta1/zz_generated.conversion.go

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

15 changes: 15 additions & 0 deletions pkg/apis/servicecatalog/v1beta1/zz_generated.deepcopy.go

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

15 changes: 15 additions & 0 deletions pkg/apis/servicecatalog/zz_generated.deepcopy.go

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

134 changes: 134 additions & 0 deletions pkg/controller/controller_binding.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"bytes"
"fmt"
"net"
"time"

"github.com/golang/glog"
osb "github.com/pmorie/go-open-service-broker-client/v2"
Expand All @@ -32,7 +33,9 @@ import (
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/util/jsonpath"
)
Expand Down Expand Up @@ -231,6 +234,18 @@ func (c *controller) reconcileServiceBindingAdd(binding *v1beta1.ServiceBinding)
return c.processServiceBindingOperationError(binding, readyCond)
}

if utilfeature.DefaultFeatureGate.Enabled(scfeatures.ServicePlanDefaults) {
// Apply default binding parameters, this must be done after we've resolved the class and plan
modified, err := c.applyDefaultBindingParameters(binding, instance)
if err != nil {
return err
}
if modified {
// the binding was updated with new parameters, so we need to continue in the next iteration
return nil
}
}

glog.V(4).Info(pcb.Message("Adding/Updating"))

request, inProgressProperties, err = c.prepareBindRequest(binding, instance)
Expand Down Expand Up @@ -268,6 +283,18 @@ func (c *controller) reconcileServiceBindingAdd(binding *v1beta1.ServiceBinding)
return c.processServiceBindingOperationError(binding, readyCond)
}

if utilfeature.DefaultFeatureGate.Enabled(scfeatures.ServicePlanDefaults) {
// Apply default binding parameters, this must be done after we've resolved the class and plan
modified, err := c.applyDefaultBindingParameters(binding, instance)
if err != nil {
return err
}
if modified {
// the binding was updated with new parameters, so we need to continue in the next iteration
return nil
}
}

glog.V(4).Info(pcb.Message("Adding/Updating"))

request, inProgressProperties, err = c.prepareBindRequest(binding, instance)
Expand Down Expand Up @@ -1134,6 +1161,113 @@ func setServiceBindingLastOperation(binding *v1beta1.ServiceBinding, operationKe
}
}

// applyDefaultBindingParameters applies any default binding parameters for a binding.
// If parameter defaults were applied, and the binding status was successfully updated, the method returns true.
// If either can not be resloved, returns an error and sets the BindingCondition with the appropriate error message.
func (c *controller) applyDefaultBindingParameters(binding *v1beta1.ServiceBinding, instance *v1beta1.ServiceInstance) (bool, error) {
// The default parameters are only applied once (though we may revisit that decision in the future depending on how
// we want to handle plan changes).
if binding.Status.DefaultBindingParameters != nil {
return false, nil
}

defaultParams, err := c.getDefaultBindingParameters(instance)
if err != nil {
return false, err
}

finalParams, err := mergeParameters(binding.Spec.Parameters, defaultParams)
if err != nil {
return false, err
}

if binding.Spec.Parameters == finalParams {
return false, nil
}

pcb := pretty.NewContextBuilder(pretty.ServiceBinding, binding.Namespace, binding.Name, "")
glog.V(4).Info(pcb.Message("Applying default binding parameters"))

binding.Spec.Parameters = finalParams
_, err = c.updateServiceBindingWithRetries(binding, func(conflictBinding *v1beta1.ServiceBinding) {
conflictBinding.Spec.Parameters = finalParams
})
if err != nil {
msg := fmt.Sprintf("error updating service binding to apply default parameters: %v", err)
glog.Warningf(pcb.Message(msg))
c.recorder.Event(binding, corev1.EventTypeWarning, errorWithParameters, msg)
return false, fmt.Errorf(msg)
}

binding.Status.DefaultBindingParameters = defaultParams
_, err = c.updateServiceBindingStatus(binding)
return true, err
}

func (c *controller) getDefaultBindingParameters(instance *v1beta1.ServiceInstance) (*runtime.RawExtension, error) {
if instance.Spec.ClusterServicePlanSpecified() {
plan, err := c.clusterServicePlanLister.Get(instance.Spec.ClusterServicePlanRef.Name)
if err != nil {
return nil, err
}
return plan.Spec.DefaultBindingParameters, nil
}

if instance.Spec.ServicePlanSpecified() {
plan, err := c.servicePlanLister.ServicePlans(instance.Namespace).Get(instance.Spec.ServicePlanRef.Name)
if err != nil {
return nil, err
}
return plan.Spec.DefaultBindingParameters, nil
}
return nil, fmt.Errorf("invalid plan reference %v", instance.Spec.PlanReference)
}

// updateServiceBindingWithRetries updates the binding
// and automatically retries if a 409 Conflict error is
// returned by the API server.
// If a conflict occurs, the provided conflictResolutionFunc is called
// so that the conflict can be resolved. There is no default universal safe
// conflict resolution logic, so conflictResolutionFunc must always be provided.
func (c *controller) updateServiceBindingWithRetries(
binding *v1beta1.ServiceBinding,
conflictResolutionFunc func(*v1beta1.ServiceBinding)) (*v1beta1.ServiceBinding, error) {

pcb := pretty.NewBindingContextBuilder(binding)

const interval = 100 * time.Millisecond
const timeout = 10 * time.Second
var updateBinding *v1beta1.ServiceBinding

bindingToUpdate := binding
err := wait.PollImmediate(interval, timeout, func() (bool, error) {
glog.V(4).Info(pcb.Message("Updating instance"))
upd, err := c.serviceCatalogClient.ServiceBindings(binding.Namespace).Update(bindingToUpdate)
if err != nil {
if !apierrors.IsConflict(err) {
return false, err
}
glog.V(4).Info(pcb.Message("Couldn't update binding because the resource was stale"))
// Fetch a fresh binding to resolve the update conflict and retry
bindingToUpdate, err = c.serviceCatalogClient.ServiceBindings(binding.Namespace).Get(binding.Name, metav1.GetOptions{})
if err != nil {
return false, err
}
conflictResolutionFunc(bindingToUpdate)
return false, nil
}

updateBinding = upd
return true, nil
})

if err != nil {
glog.Errorf(pcb.Messagef("failed to update binding: %v", err))
}

return updateBinding, err
}

// prepareBindRequest creates a bind request object to be passed to the broker
// client to create the given binding.
func (c *controller) prepareBindRequest(
Expand Down
Loading