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

Add asynchronous provisioning test using the fake broker server #923

Closed
wants to merge 1 commit into from

Conversation

arschles
Copy link
Contributor

@arschles arschles commented Jun 8, 2017

Overview

This patch creates a new test at ./test/integration/controller_instance_test.go, which exercises the controller's ReconcileInstance 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 the Instance'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

@arschles arschles added this to the 0.0.10 milestone Jun 8, 2017
@arschles arschles requested review from pmorie and derekwaynecarr June 8, 2017 19:14
@k8s-ci-robot k8s-ci-robot added the cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. label Jun 8, 2017
@arschles arschles changed the title Making the controller handle unexpected provision results Making the controller handle unexpected provision response codes Jun 8, 2017
sharedInformers.Brokers().Informer().GetStore().Add(getTestBroker())
sharedInformers.ServiceClasses().Informer().GetStore().Add(getTestServiceClass())

// Specify we want asynchronous provisioning...
Copy link
Contributor

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 {
Copy link
Contributor

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.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

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(
Copy link
Contributor

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.

Copy link
Contributor Author

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

@arschles
Copy link
Contributor Author

arschles commented Jun 9, 2017

@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.

arschles added a commit to arschles/kubernetes-service-catalog that referenced this pull request Jun 9, 2017
This is a continuation of
kubernetes-retired#533, and
is a pre-requisite for
kubernetes-retired#923
@arschles
Copy link
Contributor Author

arschles commented Jun 9, 2017

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.

pmorie pushed a commit that referenced this pull request Jun 13, 2017
* 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
@pmorie pmorie modified the milestones: 0.0.11, 0.0.10 Jun 14, 2017
@arschles arschles added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Jun 19, 2017
@arschles arschles force-pushed the unexpected-code branch 2 times, most recently from e85808e to 336a983 Compare June 20, 2017 20:28
@arschles arschles changed the title Making the controller handle unexpected provision response codes Add asynchronous provisioning test using the fake broker server Jun 20, 2017
@arschles arschles removed in-progress needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. labels Jun 20, 2017
Copy link
Contributor

@MHBauer MHBauer left a 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
Copy link
Contributor

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")
Copy link
Contributor

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?

@arschles
Copy link
Contributor Author

@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
Copy link
Contributor

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MHBauer just added

Copy link
Contributor

@MHBauer MHBauer left a 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 arschles modified the milestones: 0.0.12, 0.0.11 Jun 21, 2017
@vaikas vaikas added the LGTM2 label Jun 22, 2017
@pmorie
Copy link
Contributor

pmorie commented Jun 23, 2017

@arschles looks like this needs rebase

@pmorie pmorie added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Jun 23, 2017
@arschles arschles force-pushed the unexpected-code branch 2 times, most recently from 47dd389 to f00029e Compare June 23, 2017 19:34
@arschles arschles removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Jun 23, 2017
@arschles
Copy link
Contributor Author

@pmorie rebase done, and adaptations made to use the new OSB client that the rebase pulled in. PTAL

Copy link
Contributor

@pmorie pmorie left a 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) {
Copy link
Contributor

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 {
Copy link
Contributor

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"
Copy link
Contributor

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.

Copy link
Contributor Author

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

Copy link
Contributor

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
Copy link
Contributor

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?

Copy link
Contributor Author

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 *bools, and this is a convenience function to create them with a one-liner

Copy link
Contributor

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.

Copy link
Contributor

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.

Copy link
Contributor Author

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

Copy link
Contributor

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.

Copy link
Contributor

@pmorie pmorie left a 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
Copy link
Contributor

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"
Copy link
Contributor

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
Copy link
Contributor

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) {
Copy link
Contributor

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)
Copy link
Contributor

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

@duglin
Copy link
Contributor

duglin commented Jul 7, 2017

rebase needed

@duglin duglin added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Jul 7, 2017
@arschles arschles removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Jul 11, 2017
@arschles arschles added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Jul 19, 2017
@pmorie
Copy link
Contributor

pmorie commented Jul 21, 2017

@arschles are you planning to rebase this and land it?

@n3wscott
Copy link
Contributor

This work looks good but might need to be broken into a few smaller PRs to be able to integrate today.

@pmorie
Copy link
Contributor

pmorie commented Jan 4, 2018

@arschles can this be closed?

@nilebox
Copy link
Contributor

nilebox commented Feb 19, 2018

Closing as inactive. @arschles feel free to reopen if needed.

@nilebox nilebox closed this Feb 19, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants