-
Notifications
You must be signed in to change notification settings - Fork 382
Add asynchronous provisioning test using the fake broker server #923
Conversation
sharedInformers.Brokers().Informer().GetStore().Add(getTestBroker()) | ||
sharedInformers.ServiceClasses().Informer().GetStore().Add(getTestServiceClass()) | ||
|
||
// Specify we want asynchronous provisioning... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment seems wrong?
@@ -208,7 +208,8 @@ func (c *controller) reconcileInstance(instance *v1alpha1.Instance) error { | |||
return fmt.Errorf("Couldn't create a key for object %+v: %v", instance, err) | |||
} | |||
c.pollingQueue.Add(key) | |||
} else { | |||
} else if respCode == http.StatusCreated || respCode == http.StatusOK { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a little perplexed that this is necessary because above L163 any error should be handled and these are the only cases that return a nil error so I'm just trying to understand why this change is necessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@vaikas-google good catch. The test code is using the fake broker client instead of the real one, and the fake doesn't check for response codes like the real one does.
I'd like to keep the test intact, so I'm going to see how hard it is to retrofit a fake broker server so we can use the real client here.
} else { | ||
// the broker returned a failure response | ||
errorProvisionCalledMessage := fmt.Sprintf("provision call failed") | ||
c.updateInstanceCondition( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fwiw, i notice a consistent pattern where we ignore any error that can be returned from this.
i think we should either return, or at minimum log the error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@derekwaynecarr good catch. I'll log and return on these changed portions, and I've created #925 as a follow-up
@pmorie @vaikas-google I've put this PR back into in-progress, I may have thought about this wrong. I'll ping you for re-review if I don't end up closing this completely. |
This is a continuation of kubernetes-retired#533, and is a pre-requisite for kubernetes-retired#923
Update: I've opened #928, which implements a fake broker server and refactors a single test to use the real OSB client against the fake broker server. After that is merged, I'll convert the test herein to use the real client and the fake broker server. Given that I've seen this bug in the wild, I'm hoping the test as-written will show the buggy behavior (although I agree with you @vaikas-google that I don't see how the OSB API client could miss any status codes). If it doesn't, it'll be back to the drawing board to expose this behavior. |
* Adding a fake broker server This is a continuation of #533, and is a pre-requisite for #923 * converting a single test to use the broker server * adding boilerplate and docs * naming the pivotal brokerapi package to eliminate possible ambiguity * adding clarifying comment to the catalog requests var * adding better godocs
5651ee8
to
17a1e1e
Compare
e85808e
to
336a983
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More detail about why the broker is unsupported.
Non conforming with osbapi how?
@@ -558,6 +559,78 @@ func TestReconcileInstanceAsynchronousNoOperation(t *testing.T) { | |||
assertInstanceLastOperation(t, updatedInstance, "") | |||
} | |||
|
|||
// TestReconcileInstanceAsynchronousUnsupportedBrokerError tests to ensure that, on an asynchronous | |||
// provision, an Instance's conditions get set with a Broker failure that is not OSB API spec |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yay, test comment!
|
||
// The item should not have been added to the polling queue for later processing | ||
if testController.pollingQueue.Len() != 0 { | ||
t.Fatalf("Expected the asynchronous instance to end up in the polling queue") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This log message seems wrong?
@MHBauer I made the test comment more precise and added detail regarding the OSB spec. I think it will answer your question. |
sharedInformers.Brokers().Informer().GetStore().Add(getTestBroker()) | ||
sharedInformers.ServiceClasses().Informer().GetStore().Add(getTestServiceClass()) | ||
|
||
// Specify an error from the instance client |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this the un'expected' error? can you add that word to match the comment above?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@MHBauer just added
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@arschles thanks for clarifying.
lgtm
@arschles looks like this needs rebase |
47dd389
to
f00029e
Compare
@pmorie rebase done, and adaptations made to use the new OSB client that the rebase pulled in. PTAL |
59cfd91
to
6852321
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A couple small suggestions
|
||
fakeBrokerServerHandler.Catalog = fakebrokerserver.ConvertCatalog(getTestCatalog()) | ||
|
||
fakeKubeClient.AddReactor("get", "namespaces", func(action clientgotesting.Action) (bool, runtime.Object, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I put in a litle macro for this
// verify that 2 kubernetes actions occurred - a GET to the ServiceClass, then a GET to the | ||
// Broker | ||
kubeActions := fakeKubeClient.Actions() | ||
if e, a := 2, len(kubeActions); e != a { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can use assertNumberOfActions here too
// InstanceName is a name used for test instances | ||
InstanceName = "test-instance" | ||
// InstanceGUID is the GUID used for test instances | ||
InstanceGUID = "IGUID" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think all of these should have some indication in the name that they are the Test constants.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@MHBauer I argue that they already do when used externally to this package. Recall that the package name will come first, so this specific constant will be referenced as fake.InstanceGUID
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree that the name of the package carries the meaning. I suggest we call this package 'testfixtures', though, since the code here is not really a 'fake'.
package fake | ||
|
||
func boolPtr(b bool) *bool { | ||
return &b |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do these functions exist?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
various fields need to store *bool
s, and this is a convenience function to create them with a one-liner
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having never actually tried it, I tried it, and apparently you can't take the address of a bool. Bizzare language.
Would it be better to have it once in a toplevel util package than rewriting this in a multiple places.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have seen truePtr and falsePtr around somewhere.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, that would be nice, but out of the scope of this PR
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, those are hanging around. We'll probably want to move them here naturally at some point.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very much like where you're headed with this, a couple minor suggestions. TLDR: let's run a whole controller and drive this from informers and waiting on conditions.
limitations under the License. | ||
*/ | ||
|
||
package fake |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1000 to where you're going here, see #514
// InstanceName is a name used for test instances | ||
InstanceName = "test-instance" | ||
// InstanceGUID is the GUID used for test instances | ||
InstanceGUID = "IGUID" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree that the name of the package carries the meaning. I suggest we call this package 'testfixtures', though, since the code here is not really a 'fake'.
package fake | ||
|
||
func boolPtr(b bool) *bool { | ||
return &b |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, those are hanging around. We'll probably want to move them here naturally at some point.
|
||
// AssertInstanceReadyTrue ensures that obj is an instance and has a ready condition of True | ||
// on it | ||
func AssertInstanceReadyTrue(t *testing.T, obj runtime.Object) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to export these? The integration tests should wait for conditions to converge on an expected value, rather than making single assertions.
if testController.PollingQueueLen() != 0 { | ||
t.Fatalf("Expected polling queue to be empty") | ||
} | ||
controller.AssertAsyncOpInProgressFalse(t, updatedInstance) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's run the controller similar to the other integration tests so that are testing the behavior of a running controller instead of exercising a specific (and very clean) ordering of methods in a test harness
rebase needed |
93c6fdf
to
8f6dc37
Compare
8f6dc37
to
ee89698
Compare
@arschles are you planning to rebase this and land it? |
This work looks good but might need to be broken into a few smaller PRs to be able to integrate today. |
@arschles can this be closed? |
Closing as inactive. @arschles feel free to reopen if needed. |
Overview
This patch creates a new test at
./test/integration/controller_instance_test.go
, which exercises the controller'sReconcileInstance
function against fake clients for everything except for a broker API. This test ensures that, if the broker API returns an HTTP response with a 500 error code, the controller will write the results of that response into theInstance
's conditions.Refactors
The new test borrows a lot of logic from the existing controller unit tests, but it lives alongside the integration tests. I've done quite a bit of refactoring to enable this test to use functions that were previously only available to unit tests. Note that I've done the least amount of refactoring I had to, but there are still quite a few changes here. The result is that the unit test code is slightly more factored and reusable as well.
Follow-ups
While this PR only adds a single additional test, I plan to do several further follow-ups to add several additional tests that would best be written as this type of integration test (such as #984 and #985). The result of those additions will be further (but smaller) refactors and, of course, additional test coverage.
Related Issues
See #914 for details on motivations behind this test.
Partially addresses #914
Requires #928