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

Commit 1877c1f

Browse files
pmoriearschles
authored andcommitted
Bind only once the instance becomes ready (#747)
1 parent fc2d41a commit 1877c1f

File tree

2 files changed

+118
-1
lines changed

2 files changed

+118
-1
lines changed

pkg/controller/controller.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@ const (
269269
errorWithOngoingAsyncOperation string = "ErrorAsyncOperationInProgress"
270270
errorWithOngoingAsyncOperationMessage string = "Another operation for this service instance is in progress. "
271271
errorNonbindableServiceClassReason string = "ErrorNonbindableServiceClass"
272+
errorInstanceNotReadyReason string = "ErrorInstanceNotReady"
272273

273274
successInjectedBindResultReason string = "InjectedBindResult"
274275
successInjectedBindResultMessage string = "Injected bind result"
@@ -1247,6 +1248,20 @@ func (c *controller) reconcileBinding(binding *v1alpha1.Binding) error {
12471248
return err
12481249
}
12491250

1251+
if !isInstanceReady(instance) {
1252+
s := fmt.Sprintf(`Binding cannot begin because referenced instance "%v/%v" is not ready`, instance.Namespace, instance.Name)
1253+
glog.Info(s)
1254+
c.updateBindingCondition(
1255+
binding,
1256+
v1alpha1.BindingConditionReady,
1257+
v1alpha1.ConditionFalse,
1258+
errorInstanceNotReadyReason,
1259+
s,
1260+
)
1261+
c.recorder.Eventf(binding, api.EventTypeWarning, errorInstanceNotReadyReason, s)
1262+
return err
1263+
}
1264+
12501265
request := &brokerapi.BindingRequest{
12511266
ServiceID: serviceClass.OSBGUID,
12521267
PlanID: servicePlan.OSBGUID,
@@ -1730,3 +1745,15 @@ func unmarshalParameters(in []byte) (map[string]interface{}, error) {
17301745
}
17311746
return parameters, nil
17321747
}
1748+
1749+
// isInstanceReady returns whether the given instance has a ready condition
1750+
// with status true.
1751+
func isInstanceReady(instance *v1alpha1.Instance) bool {
1752+
for _, cond := range instance.Status.Conditions {
1753+
if cond.Type == v1alpha1.InstanceConditionReady {
1754+
return cond.Status == v1alpha1.ConditionTrue
1755+
}
1756+
}
1757+
1758+
return false
1759+
}

pkg/controller/controller_test.go

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,19 @@ func getTestNonbindableInstance() *v1alpha1.Instance {
284284
return i
285285
}
286286

287+
func getTestInstanceWithStatus(status v1alpha1.ConditionStatus) *v1alpha1.Instance {
288+
instance := getTestInstance()
289+
instance.Status = v1alpha1.InstanceStatus{
290+
Conditions: []v1alpha1.InstanceCondition{{
291+
Type: v1alpha1.InstanceConditionReady,
292+
Status: status,
293+
LastTransitionTime: metav1.NewTime(time.Now().Add(-5 * time.Minute)),
294+
}},
295+
}
296+
297+
return instance
298+
}
299+
287300
// getTestInstanceAsync returns an instance in async mode
288301
func getTestInstanceAsyncProvisioning(operation string) *v1alpha1.Instance {
289302
instance := getTestInstance()
@@ -1925,7 +1938,7 @@ func TestReconcileBindingWithParameters(t *testing.T) {
19251938

19261939
sharedInformers.Brokers().Informer().GetStore().Add(getTestBroker())
19271940
sharedInformers.ServiceClasses().Informer().GetStore().Add(getTestServiceClass())
1928-
sharedInformers.Instances().Informer().GetStore().Add(getTestInstance())
1941+
sharedInformers.Instances().Informer().GetStore().Add(getTestInstanceWithStatus(v1alpha1.ConditionTrue))
19291942

19301943
binding := &v1alpha1.Binding{
19311944
ObjectMeta: metav1.ObjectMeta{Name: testBindingName, Namespace: testNamespace},
@@ -2098,6 +2111,53 @@ func TestReconcileBindingFailsWithInstanceAsyncOngoing(t *testing.T) {
20982111
}
20992112
}
21002113

2114+
func TestReconcileBindingInstanceNotReady(t *testing.T) {
2115+
fakeKubeClient, fakeCatalogClient, fakeBrokerClient, testController, sharedInformers := newTestController(t)
2116+
2117+
fakeBrokerClient.CatalogClient.RetCatalog = getTestCatalog()
2118+
2119+
fakeKubeClient.AddReactor("get", "namespaces", func(action clientgotesting.Action) (bool, runtime.Object, error) {
2120+
return true, &v1.Namespace{
2121+
ObjectMeta: metav1.ObjectMeta{
2122+
UID: types.UID("test_ns_uid"),
2123+
},
2124+
}, nil
2125+
})
2126+
2127+
sharedInformers.Brokers().Informer().GetStore().Add(getTestBroker())
2128+
sharedInformers.ServiceClasses().Informer().GetStore().Add(getTestServiceClass())
2129+
sharedInformers.Instances().Informer().GetStore().Add(getTestInstance())
2130+
2131+
binding := &v1alpha1.Binding{
2132+
ObjectMeta: metav1.ObjectMeta{Name: testBindingName, Namespace: testNamespace},
2133+
Spec: v1alpha1.BindingSpec{
2134+
InstanceRef: v1.LocalObjectReference{Name: testInstanceName},
2135+
OSBGUID: bindingGUID,
2136+
},
2137+
}
2138+
2139+
testController.reconcileBinding(binding)
2140+
2141+
if _, ok := fakeBrokerClient.Bindings[fakebrokerapi.BindingsMapKey(instanceGUID, bindingGUID)]; ok {
2142+
t.Fatalf("Unexpected broker binding call")
2143+
}
2144+
2145+
actions := fakeCatalogClient.Actions()
2146+
assertNumberOfActions(t, actions, 1)
2147+
2148+
// There should only be one action that says binding was created
2149+
updatedBinding := assertUpdateStatus(t, actions[0], binding)
2150+
assertBindingReadyFalse(t, updatedBinding)
2151+
2152+
events := getRecordedEvents(testController)
2153+
assertNumEvents(t, events, 1)
2154+
2155+
expectedEvent := api.EventTypeWarning + " " + errorInstanceNotReadyReason + " " + `Binding cannot begin because referenced instance "test-ns/test-instance" is not ready`
2156+
if e, a := expectedEvent, events[0]; e != a {
2157+
t.Fatalf("Received unexpected event: %v", a)
2158+
}
2159+
}
2160+
21012161
func TestReconcileBindingNamespaceError(t *testing.T) {
21022162
fakeKubeClient, fakeCatalogClient, fakeBrokerClient, testController, sharedInformers := newTestController(t)
21032163

@@ -2499,6 +2559,36 @@ func TestCatalogConversionMultipleServiceClasses(t *testing.T) {
24992559

25002560
}
25012561

2562+
func TestIsBrokerReady(t *testing.T) {
2563+
cases := []struct {
2564+
name string
2565+
input *v1alpha1.Instance
2566+
ready bool
2567+
}{
2568+
{
2569+
name: "ready",
2570+
input: getTestInstanceWithStatus(v1alpha1.ConditionTrue),
2571+
ready: true,
2572+
},
2573+
{
2574+
name: "no status",
2575+
input: getTestInstance(),
2576+
ready: false,
2577+
},
2578+
{
2579+
name: "not ready",
2580+
input: getTestInstanceWithStatus(v1alpha1.ConditionFalse),
2581+
ready: false,
2582+
},
2583+
}
2584+
2585+
for _, tc := range cases {
2586+
if e, a := tc.ready, isInstanceReady(tc.input); e != a {
2587+
t.Errorf("%v: expected result %v, got %v", tc.name, e, a)
2588+
}
2589+
}
2590+
}
2591+
25022592
// newTestController creates a new test controller injected with fake clients
25032593
// and returns:
25042594
//

0 commit comments

Comments
 (0)