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

Commit 7b9f8bd

Browse files
staeblercarolynvs
authored andcommitted
Re-work TestCreateServiceInstanceWithProvisionFailure to mitigate its flake (#2153)
* Re-work the TestCreateServiceInstanceWithProvisionFailure integration test to mitigate its flake * Revert change to AssertServiceInstanceCondition
1 parent 6ab3ad7 commit 7b9f8bd

File tree

2 files changed

+159
-81
lines changed

2 files changed

+159
-81
lines changed

test/integration/controller_instance_test.go

Lines changed: 146 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1005,72 +1005,70 @@ func (e TimeoutError) Error() string {
10051005

10061006
// TestCreateServiceInstanceWithProvisionFailure tests creating a ServiceInstance
10071007
// with various failure results in response to the provision request.
1008-
// TODO(carolynvs): I'm disabling this test because it's a flake that fails so much that I'm seeing 🔥
1009-
func FlakeTestCreateServiceInstanceWithProvisionFailure(t *testing.T) {
1008+
func TestCreateServiceInstanceWithProvisionFailure(t *testing.T) {
10101009
cases := []struct {
1011-
name string
1012-
statusCode int
1013-
nonHTTPResponseError error
1014-
conditionReason string
1015-
expectFailCondition bool
1010+
// name of the test
1011+
name string
1012+
// status code returned by failing provision calls
1013+
statusCode int
1014+
// non-HTTP error returned by failing provision calls
1015+
nonHTTPResponseError error
1016+
// expected reason used in the instance condition to indiciate that the provision failed
1017+
provisionErrorReason string
1018+
// expected reason used in the instance condiiton to indicate that the provision failed terminally
1019+
failReason string
1020+
// true if the failed provision is expected to trigger orphan mitigation
10161021
triggersOrphanMitigation bool
10171022
}{
10181023
{
1019-
name: "Status OK",
1020-
statusCode: http.StatusOK,
1021-
conditionReason: "ProvisionedSuccessfully",
1022-
expectFailCondition: false,
1024+
name: "Status OK",
1025+
statusCode: http.StatusOK,
1026+
provisionErrorReason: "ProvisionCallFailed",
10231027
},
10241028
{
10251029
name: "Status Created",
10261030
statusCode: http.StatusCreated,
1027-
conditionReason: "ProvisionedSuccessfully",
1028-
expectFailCondition: false,
1031+
provisionErrorReason: "ProvisionCallFailed",
10291032
triggersOrphanMitigation: true,
10301033
},
10311034
{
10321035
name: "other 2xx",
10331036
statusCode: http.StatusNoContent,
1034-
conditionReason: "ProvisionedSuccessfully",
1035-
expectFailCondition: false,
1037+
provisionErrorReason: "ProvisionCallFailed",
10361038
triggersOrphanMitigation: true,
10371039
},
10381040
{
1039-
name: "3XX",
1040-
statusCode: 300,
1041-
conditionReason: "ProvisionedSuccessfully",
1042-
expectFailCondition: false,
1041+
name: "3XX",
1042+
statusCode: 300,
1043+
provisionErrorReason: "ProvisionCallFailed",
10431044
},
10441045
{
10451046
name: "Status Request Timeout",
10461047
statusCode: http.StatusRequestTimeout,
1047-
conditionReason: "ProvisionedSuccessfully",
1048-
expectFailCondition: false,
1048+
provisionErrorReason: "ProvisionCallFailed",
10491049
triggersOrphanMitigation: false,
10501050
},
10511051
{
1052-
name: "400",
1053-
statusCode: 400,
1054-
conditionReason: "ClusterServiceBrokerReturnedFailure",
1055-
expectFailCondition: true,
1052+
name: "400",
1053+
statusCode: 400,
1054+
provisionErrorReason: "ProvisionCallFailed",
1055+
failReason: "ClusterServiceBrokerReturnedFailure",
10561056
},
10571057
{
1058-
name: "other 4XX",
1059-
statusCode: 403,
1060-
conditionReason: "ProvisionedSuccessfully",
1061-
expectFailCondition: false,
1058+
name: "other 4XX",
1059+
statusCode: 403,
1060+
provisionErrorReason: "ProvisionCallFailed",
10621061
},
10631062
{
10641063
name: "5XX",
10651064
statusCode: 500,
1066-
conditionReason: "ProvisionedSuccessfully",
1067-
expectFailCondition: false,
1065+
provisionErrorReason: "ProvisionCallFailed",
10681066
triggersOrphanMitigation: true,
10691067
},
10701068
{
10711069
name: "non-url transport error",
10721070
nonHTTPResponseError: fmt.Errorf("non-url error"),
1073-
conditionReason: "ProvisionedSuccessfully",
1071+
provisionErrorReason: "ErrorCallingProvision",
10741072
},
10751073
{
10761074
name: "non-timeout url error",
@@ -1079,7 +1077,7 @@ func FlakeTestCreateServiceInstanceWithProvisionFailure(t *testing.T) {
10791077
URL: "https://fakebroker.com/v2/service_instances/instance_id",
10801078
Err: fmt.Errorf("non-timeout error"),
10811079
},
1082-
conditionReason: "ProvisionedSuccessfully",
1080+
provisionErrorReason: "ErrorCallingProvision",
10831081
},
10841082
{
10851083
name: "network timeout",
@@ -1088,14 +1086,26 @@ func FlakeTestCreateServiceInstanceWithProvisionFailure(t *testing.T) {
10881086
URL: "https://fakebroker.com/v2/service_instances/instance_id",
10891087
Err: TimeoutError("timeout error"),
10901088
},
1091-
conditionReason: "ProvisionedSuccessfully",
1092-
expectFailCondition: false,
1089+
provisionErrorReason: "ErrorCallingProvision",
10931090
triggersOrphanMitigation: true,
10941091
},
10951092
}
10961093
for _, tc := range cases {
10971094
tc := tc
10981095
t.Run(tc.name, func(t *testing.T) {
1096+
1097+
provisionSuccessChan := make(chan bool, 2)
1098+
deprovisionSuccessChan := make(chan bool, 2)
1099+
deprovisionBlockChan := make(chan bool, 2)
1100+
1101+
// Ensure that broker requests respond successfully after running
1102+
// the core of the test so that the resource cleanup can proceed.
1103+
defer func() {
1104+
provisionSuccessChan <- true
1105+
deprovisionSuccessChan <- true
1106+
deprovisionBlockChan <- false
1107+
}()
1108+
10991109
//t.Parallel()
11001110
ct := &controllerTest{
11011111
t: t,
@@ -1111,72 +1121,127 @@ func FlakeTestCreateServiceInstanceWithProvisionFailure(t *testing.T) {
11111121
Description: strPtr("response description"),
11121122
}
11131123
}
1124+
respondSuccessfullyToProvision := false
11141125
ct.osbClient.ProvisionReaction = fakeosb.DynamicProvisionReaction(
1115-
getProvisionResponseByPollCountReactions(2, []fakeosb.ProvisionReaction{
1116-
fakeosb.ProvisionReaction{
1117-
Error: reactionError,
1118-
},
1119-
fakeosb.ProvisionReaction{
1120-
Response: &osb.ProvisionResponse{},
1121-
},
1122-
}))
1126+
func(r *osb.ProvisionRequest) (*osb.ProvisionResponse, error) {
1127+
select {
1128+
case respondSuccessfullyToProvision = <-provisionSuccessChan:
1129+
default:
1130+
}
1131+
if respondSuccessfullyToProvision {
1132+
return &osb.ProvisionResponse{}, nil
1133+
} else {
1134+
return nil, reactionError
1135+
}
1136+
})
1137+
respondSuccessfullyToDeprovision := false
1138+
blockDeprovision := true
11231139
ct.osbClient.DeprovisionReaction = fakeosb.DynamicDeprovisionReaction(
1124-
getDeprovisionResponseByPollCountReactions(2, []fakeosb.DeprovisionReaction{
1125-
fakeosb.DeprovisionReaction{
1126-
Error: osb.HTTPStatusCodeError{
1140+
func(r *osb.DeprovisionRequest) (*osb.DeprovisionResponse, error) {
1141+
for blockDeprovision {
1142+
blockDeprovision = <-deprovisionBlockChan
1143+
}
1144+
select {
1145+
case respondSuccessfullyToDeprovision = <-deprovisionSuccessChan:
1146+
default:
1147+
}
1148+
if respondSuccessfullyToDeprovision {
1149+
return &osb.DeprovisionResponse{}, nil
1150+
} else {
1151+
return nil, osb.HTTPStatusCodeError{
11271152
StatusCode: 500,
11281153
ErrorMessage: strPtr("temporary deprovision error"),
1129-
},
1130-
},
1131-
fakeosb.DeprovisionReaction{
1132-
Response: &osb.DeprovisionResponse{},
1133-
},
1134-
}))
1154+
}
1155+
}
1156+
})
11351157
},
11361158
}
11371159
ct.run(func(ct *controllerTest) {
1138-
var condition v1beta1.ServiceInstanceCondition
1139-
if tc.expectFailCondition {
1140-
// Instance should get stuck in a Failed condition
1141-
condition = v1beta1.ServiceInstanceCondition{
1142-
Type: v1beta1.ServiceInstanceConditionFailed,
1143-
Status: v1beta1.ConditionTrue,
1144-
Reason: tc.conditionReason,
1145-
}
1146-
} else {
1147-
// Instance provisioning should be retried and succeed
1148-
condition = v1beta1.ServiceInstanceCondition{
1149-
Type: v1beta1.ServiceInstanceConditionReady,
1150-
Status: v1beta1.ConditionTrue,
1151-
Reason: tc.conditionReason,
1152-
}
1160+
// Wait for the provision to fail
1161+
condition := v1beta1.ServiceInstanceCondition{
1162+
Type: v1beta1.ServiceInstanceConditionReady,
1163+
Status: v1beta1.ConditionFalse,
1164+
Reason: tc.provisionErrorReason,
1165+
}
1166+
if tc.triggersOrphanMitigation {
1167+
condition.Reason = "StartingInstanceOrphanMitigation"
11531168
}
11541169
if err := util.WaitForInstanceCondition(ct.client, testNamespace, testInstanceName, condition); err != nil {
1155-
t.Fatalf("error waiting for instance condition: %v", err)
1170+
t.Fatalf("error waiting for provision to fail: %v", err)
11561171
}
11571172

1158-
if tc.expectFailCondition {
1159-
if err := util.WaitForInstanceProcessedGeneration(ct.client, ct.instance.Namespace, ct.instance.Name, 1); err != nil {
1160-
t.Fatalf("error waiting for instance reconciliation to complete: %v", err)
1161-
}
1173+
// Assert that the latest generation has been observed
1174+
instance, err := ct.client.ServiceInstances(testNamespace).Get(testInstanceName, metav1.GetOptions{})
1175+
if err != nil {
1176+
t.Fatalf("error getting instance: %v", err)
1177+
}
1178+
if e, a := int64(1), instance.Status.ObservedGeneration; e != a {
1179+
t.Fatalf("unexpected observed generation: expected %v, got %v", e, a)
11621180
}
11631181

1164-
brokerActions := ct.osbClient.Actions()
1165-
fmt.Printf("%#v", brokerActions)
1182+
// If the provision failed with a terminating failure
1183+
if tc.failReason != "" {
1184+
util.AssertServiceInstanceCondition(t, instance, v1beta1.ServiceInstanceConditionFailed, v1beta1.ConditionTrue, tc.failReason)
1185+
if e, a := 0, len(findBrokerActions(t, ct.osbClient, fakeosb.DeprovisionInstance)); e != a {
1186+
t.Fatalf("unexpected calls to deprovision instance: expected %v, got %v", e, a)
1187+
}
1188+
return
1189+
}
11661190

1167-
// Ensure that we meet expectations on deprovision requests for orphan mitigation
1168-
deprovisionActions := findBrokerActions(t, ct.osbClient, fakeosb.DeprovisionInstance)
1191+
// Assert that the orphan mitigation reason was set correctly
11691192
if tc.triggersOrphanMitigation {
1170-
if len(deprovisionActions) == 0 {
1171-
t.Fatal("expected orphan mitigation deprovision request to occur")
1193+
util.AssertServiceInstanceCondition(t, instance, v1beta1.ServiceInstanceConditionOrphanMitigation, v1beta1.ConditionTrue, tc.provisionErrorReason)
1194+
if !instance.Status.OrphanMitigationInProgress {
1195+
t.Fatalf("expected orphan mitigation to be in progress")
11721196
}
11731197
} else {
1174-
if len(deprovisionActions) != 0 {
1175-
t.Fatal("unexpected deprovision requests")
1198+
util.AssertServiceInstanceConditionFalseOrAbsent(t, instance, v1beta1.ServiceInstanceConditionOrphanMitigation)
1199+
if instance.Status.OrphanMitigationInProgress {
1200+
t.Fatalf("expected orphan mitigation to not be in progress")
1201+
}
1202+
}
1203+
1204+
// Now that the provision error conditions have been asserted, allow the broker to send its response to the deprovision request
1205+
deprovisionBlockChan <- false
1206+
1207+
if tc.triggersOrphanMitigation {
1208+
// Assert that the ready condition is set to Unknown when the deprovision request fails
1209+
if err := util.WaitForInstanceCondition(ct.client, testNamespace, testInstanceName, v1beta1.ServiceInstanceCondition{
1210+
Type: v1beta1.ServiceInstanceConditionReady,
1211+
Status: v1beta1.ConditionUnknown,
1212+
}); err != nil {
1213+
t.Fatalf("error waiting for instance deprovision to fail: %v", err)
11761214
}
11771215
}
11781216

1179-
// All instances should eventually succeed
1217+
// Now that everything surround the failed provision has been asserted, allow provision requests
1218+
// to succeed. Also, allow orphan mitigation to complete by allowing deprovision requests to succeed.
1219+
provisionSuccessChan <- true
1220+
deprovisionSuccessChan <- true
1221+
1222+
// Wait for the instance to be provisioned successfully
1223+
if err := util.WaitForInstanceCondition(ct.client, testNamespace, testInstanceName, v1beta1.ServiceInstanceCondition{
1224+
Type: v1beta1.ServiceInstanceConditionReady,
1225+
Status: v1beta1.ConditionTrue,
1226+
}); err != nil {
1227+
t.Fatalf("error waiting for instance to be provisioned: %v", err)
1228+
}
1229+
1230+
// Assert that the observed generation is up-to-date, that orphan mitigation is not in progress,
1231+
// and that the instance is not in a failed state.
1232+
instance, err = ct.client.ServiceInstances(testNamespace).Get(testInstanceName, metav1.GetOptions{})
1233+
if err != nil {
1234+
t.Fatalf("error getting instance: %v", err)
1235+
}
1236+
if g, og := instance.Generation, instance.Status.ObservedGeneration; g != og {
1237+
t.Fatalf("latest generation not observed: generation: %v, observed: %v", g, og)
1238+
}
1239+
if instance.Status.OrphanMitigationInProgress {
1240+
t.Fatalf("unexpected orphan mitigation in progress")
1241+
}
1242+
util.AssertServiceInstanceConditionFalseOrAbsent(t, instance, v1beta1.ServiceInstanceConditionFailed)
1243+
1244+
// Assert that the last broker action was a provision-instance request.
11801245
getLastBrokerAction(t, ct.osbClient, fakeosb.ProvisionInstance)
11811246
})
11821247
})

test/util/util.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,3 +369,16 @@ func AssertServiceBindingCondition(t *testing.T, binding *v1beta1.ServiceBinding
369369
t.Fatalf("%v condition not found", conditionType)
370370
}
371371
}
372+
373+
// AssertServiceInstanceConditionFalseOrAbsent asserts that the instance's status
374+
// either contains the given condition type with a status of False or does not
375+
// contain the given condition.
376+
func AssertServiceInstanceConditionFalseOrAbsent(t *testing.T, instance *v1beta1.ServiceInstance, conditionType v1beta1.ServiceInstanceConditionType) {
377+
for _, condition := range instance.Status.Conditions {
378+
if condition.Type == conditionType {
379+
if e, a := v1beta1.ConditionFalse, condition.Status; e != a {
380+
t.Fatalf("%v condition had unexpected status; expected %v, got %v", conditionType, e, a)
381+
}
382+
}
383+
}
384+
}

0 commit comments

Comments
 (0)