Skip to content

integration/plugin/authz: port tests from integration-cli#34941

Merged
vdemeester merged 3 commits intomoby:masterfrom
dsheets:authz-tests-api-port
Oct 11, 2017
Merged

integration/plugin/authz: port tests from integration-cli#34941
vdemeester merged 3 commits intomoby:masterfrom
dsheets:authz-tests-api-port

Conversation

@dsheets
Copy link
Copy Markdown
Contributor

@dsheets dsheets commented Sep 22, 2017

- What I did

I ported the authz plugin tests from integration-cli using the CLI to integration/plugin/authz using the API.

At least some of this work is required for #33658 and #33375.

- How I did it

I mechanically changed the structure of the tests to fit the new API test style, replaced CLI use with API helper utility function calls, and then fixed up build and test failures.

- How to verify it

The tests execute and pass. Side-by-side visual comparison can demonstrate that they test the functionally equivalent behaviors.

- Description for the changelog

N/A

- A picture of a cute animal (not mandatory but encouraged)

long-tailed planigale

Planigale ingrami, or the long-tailed planigale, is believed to be the smallest marsupial on Earth. It is native to Australia's Top End where it is abundant but rarely seen. The average weight for males is 4.2g and for females is 4.3g.

/cc @dnephin @thaJeztah

@thaJeztah
Copy link
Copy Markdown
Member

Thanks! Looks like there's some linting failures;

14:07:06 integration/internal/api/image/image.go:12:1:warning: exported function Save should have comment or be unexported (golint)
14:07:06 integration/internal/api/image/image.go:25:2:warning: 'if err != nil { return err }; return nil' can be simplified to 'return err' (S1013) (gosimple)
14:07:06 integration/plugin/authz/authz_plugin_test.go:344:13:warning: ineffectual assignment to err (ineffassign)
14:07:06 integration/util/requirement/requirement.go:14:29:warning: unnecessary conversion (unconvert)

@dsheets
Copy link
Copy Markdown
Contributor Author

dsheets commented Sep 22, 2017

Yes, I've just fixed those. For some reason, make validate hangs on my machine recently. It would be really great to have some documentation of exactly what tests are run by CI and how. I've had make validate pass in the past but CI's linting fail. I also have tested this PR successfully locally with something like DOCKER_GRAPHDRIVER=vfs TESTFLAGS='-test.run TestAuthZPlugin*' make test-integration.

@dnephin
Copy link
Copy Markdown
Member

dnephin commented Sep 22, 2017

#34911 should make it a lot more obvious about what is run by CI. Needs another LGTM to merge

I've had make validate pass in the past but CI's linting fail.

If you encounter this again please let me know

Copy link
Copy Markdown
Member

@dnephin dnephin left a comment

Choose a reason for hiding this comment

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

Thanks for porting this test suite. Mostly looks good, but I don't think integration/internal/api/ should exist. Most of these functions are ` line wrappers around the api client which just hides the context. We should be testing the API more directly, not wrapping our client library with these helpers.

In a few cases there is some logic that can be extracted into factories for building the options struct, or helpers for transforming the responses, but I don't think they should wrap the client.

I think it's also better that we keep these as unexported. There should be very little re-use of these between test suites because each suite should be focused on just the component under test.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It seems strange to panic here, especially on a timeout.

How about add a t LogT argument to this function and do this instead:

t.Logf("Timeout ...")
return false

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This comes directly from https://github.com/moby/moby/blob/master/integration-cli/requirements_test.go#L106 which I did not write. I don't think tests that require hub connectivity should allow test runs to pass by skipping them and emitting a logging message that will be missed/ignored in the thousands of lines of spew.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We should fix these problem instead of carrying them over into the new test suite.

I don't think tests that require hub connectivity should allow test runs to pass by skipping them and emitting a logging message that will be missed/ignored in the thousands of lines of spew.

If you think this should be a fatal error you can use t.Fatal(), but the purpose of these helpers is to detect state and skip tests that can't be run, so I don't think that's really appropriate. Skipping the test is the right behaviour. There is no reason to panic here.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I don't think testdata should be a package. If this is because hack/make/test-integration tries to run this as a test package how about we just add testdata to the exclude list?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

also doesn't need to be a package

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Shouldn't we clean even if d == nil ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We merged a PR recently that added a ProtectAll() I think that should be used now. We should unexport the rest of these Protect*() method so it's more obvious.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

If we used ProtectAll, wouldn't that negate the use of testEnv.Clean? I think that containers and plugins should probably be cleaned up after these tests, no?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

ProtectAll() is maybe a misleading name. All it does is get a list of all the existing objects, so that Clean() doesn't remove them later. This is important for testing EE because it has system containers that it needs to keep running.

Clean() will still remove anything created by the tests.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

internal shouldn't need a main_test.go

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We should not be maintaining an abstraction over the APIClient for tests. Please remove this file. The only logic that really needs to be a new function is namesFromVolumes() which can be an unexported helper next to the one place it is used.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

While it may be desirable in the long term to not have any abstraction over APIClient for tests, this design makes porting CLI tests much easier and reduces incidental complexity significantly. To maintain correctness, I recommend not attempting to simultaneously port tests and radically change them or instill new best practices.

Across existing tests, similar functionality will be required:

$ git grep "volume\", \"ls\")" integration-cli/ | wc -l
      13
$ git grep "volume\", \"ls" integration-cli/ | wc -l
      42

As this functionality is in integration/internal, golang's magic export scoping rules for internal will prevent users outside of this repository.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Please delete, this doesn't need to exist, just call client.ServerVersion() directly.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This exists to provide a homogeneous CLI replacement interface.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is exactly the problem. We should not be testing as if we had a CLI. We need to test the API as directly as possible, not mimic CLI behaviour.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

As explained in my PR comment, that is a laudable possible goal for the future but these tests were written against the CLI.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I guess I don't understand why we're moving these tests to integration/ if they are going to be nearly identical to the integration-cli tests

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

My understanding is that we're moving the tests to eliminate the moby/moby ➡️ docker/cli dependency and unblock additional test contributions as integration-cli/ is now deprecated.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This can be an unexported helper next to the one place it is used.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It won't be used in one place for long. The integration-cli test suite has significant machinery for event stream handling.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We should only be testing events from the integration/system package, so it doesn't need to be exported.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

A past authz test author clearly felt that it was necessary to test the interaction between events and authz plugins. In the future, it may be an improvement to move this test elsewhere but in the meantime it seems prudent test the integration between disparate components with the more complicated to test component. Once integation-cli/ has been eliminated, it should be easy to review all the places where event functionality is used.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This can be an unexported helper in the one file where it is used.

The rest of the file should be removed, they are one line wrappers around the client.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This will be used by other test suites -- at least mount point plugins. The other functions in this file exist to provide 1:1 mapping to the command line without depending on docker/cli. The client library imposes significant incidental complexity (contexts, options, questionable semantic decisions like in PluginInstall, etc) that a developer writing or porting a test shouldn't have to be subjected to. If, eventually, there is actually no benefit to fixing up the APIClient interface for test suite users, great. Until that time, this design makes it much easier to work with by e.g. not requiring questions like "Is there a utility function for this or is this one of the annoying APIClient functions?"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

1:1 mapping to the command line

This is exactly what we do not want. If we were going to provide a 1:1 mapping to the CLI there would be little value in removing the CLI from the test suite.

If there are helpers that will be shared between the plugin suite then maybe create an integration/plugin/internal ?

Copy link
Copy Markdown
Member

@dnephin dnephin left a comment

Choose a reason for hiding this comment

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

Test helpers which accept t *testing.T as the first argument instead of returning error are also really nice. If we keep some of these helpers I would suggest passing in testing.T, which will help keep the tests readable and not distract from the relevant lines in the test.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This can be replaced with a ImageImportOptionsFromPath(path string) types.ImageImportOptions

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

git grep ImageImportOptionsFromPath returns no results for me. It also doesn't look like it has a compatible signature. These functions are intended to be analogous to CLI invocations.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can be replaced with a saveToPath(source io.Reader, path string) err

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It's not clear to me where saveToPath is implemented or how it can be a replacement with such a different signature.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Looks like the same saveToPath() ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It's not clear to me where saveToPath is implemented or how it can be a replacement with such a different signature.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I was thinking it would be implemented in integration/plugin/authz as an unexported helper. Sorry I didn't provide the full signature:

savetoPath(t *testing.T, content io.ReadCloser) string

The return value is the path to the file.

Copy link
Copy Markdown
Member

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 can be replaced with helpers that return container.Config() instead.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This would break the predictable homogeneity and make invocations of what used to be docker run much more verbose.

@dsheets dsheets requested a review from tianon as a code owner September 25, 2017 12:55
@dsheets
Copy link
Copy Markdown
Contributor Author

dsheets commented Sep 25, 2017

Thanks for porting this test suite. Mostly looks good, but I don't think integration/internal/api/ should exist. Most of these functions are ` line wrappers around the api client which just hides the context. We should be testing the API more directly, not wrapping our client library with these helpers.

The tests being ported are CLI tests. These wrappers homogenize the API client interface to make it behave more like the CLI interface and improve the porting experience.

In a few cases there is some logic that can be extracted into factories for building the options struct, or helpers for transforming the responses, but I don't think they should wrap the client.

Why not? The tests do not demand most of the functionality that the API offers and which would clutter the tests with irrelevant noise.

I think it's also better that we keep these as unexported. There should be very little re-use of these between test suites because each suite should be focused on just the component under test.

As the CLI -> API library is located in internal, it can't be used outside of the integration tests. My cursory grepping of the repository indicates that many CLI tests use a variety of commands that do not correspond directly to the specific functionality under test.

Do you have a document describing the integration-cli -> integration test migration plan?

Could you show me an integration-cli test suite you have ported that demonstrates how you'd like the port to be done and how to reduce the chance of defects during the port?

@dnephin
Copy link
Copy Markdown
Member

dnephin commented Sep 27, 2017

The tests being ported are CLI tests. These wrappers homogenize the API client interface to make it behave more like the CLI

Exactly, that's the problem.

The tests do not demand most of the functionality that the API offers and which would clutter the tests with irrelevant noise.

These are tests of the API. If you hide how the API is called the test loses a lot of value, because it's no longer clear what you are testing. The helpers may even silently break the test by masking the actual API behaviour. I agree that implementation details (like complex setup, and complex assertions) should be hidden from the testcase function, but the actual API call needs to be there, because that is what is being tested.

many CLI tests use a variety of commands that do not correspond directly to the specific functionality under test.

Another major problem that will hopefully be fixed in the integration/ suite.

Do you have a document describing the integration-cli -> integration test migration plan?

There are a couple, but I will admit they are probably not as thorough as they could be. It is hard to enumerate all the problems with the old suite.

Could you show me an integration-cli test suite you have ported that demonstrates how you'd like the port to be done and how to reduce the chance of defects during the port?

https://github.com/moby/moby/blob/master/integration/service/inspect_test.go there is also a test for version, but it is a very simple case.

Testing plugins seems to be more involved because it requires a daemon to be started with specific flags (which is not true of most of the other API endpoints).

An important detail is that the API endpoint that is being tested is central to the test, and any setup/teardown is (as much as possible) extracted out into helpers with appropriately descriptive names. I think for the most part the tests here fit that criteria, the only different is the abstraction over the API calls.

If there are some common helpers that will be used between plugins, integration/plugin/internal might be a good place for them.

@dnephin
Copy link
Copy Markdown
Member

dnephin commented Sep 27, 2017

The plugin tests are also a very different case because they are actually testing API calls into other components. Some of these helpers for plugins are appropriate, but if we expose them to all of integration/ they will be re-used incorrectly.

The ideal way to test these plugins would really be to re-use the test suite for the actual component. If we had the tests annotated correctly we could spin up a daemon that has the plugin loaded and then trigger the component test suite against that daemon. With the right test requirements/skips setup we wouldn't have to write a second test suite. We would have just a couple extra test cases for the "disallowed by plugin" case, which would get skipped when run against a non-plugin daemon.

I think that's probably unlikely to happen, so this suite is necessary.

@dsheets
Copy link
Copy Markdown
Contributor Author

dsheets commented Sep 28, 2017

It sounds like you have a lot of ideas for how moby/moby integration tests should work in the future. I opened this PR as it is all but required to satisfactorily test #33658. The goal here is reuse of existing test functionality to expand the authz test suite in a future-friendly way. I anticipate that this process of porting CLI tests to be independent of docker/cli will be required by other contributors who need to modify tests or contribute new tests. To that end, my goal is to make it as easy as possible to port CLI tests to be independent of docker/cli. This has several benefits:

  1. integration-cli is drawn down and eliminated sooner as porting is easy
  2. dependence on docker/cli is eliminated sooner
  3. almost any contributor can port tests as the process is largely mechanical
  4. incremental quality improvement is realized
  5. the correspondence between CLI-esque and API use is immediately visible
  6. any later SDK or client library author can see exactly and immediately the invocations necessary to achieve a CLI-like result
  7. command executions are now named with symbols so they can be easily changed in the future

I think it's great that we have some ideas about how to improve the test suite in the future. In the present, having a mostly empty 'new' test suite and a large but deprecated 'old' test suite is negative for the project's health.

The existing tests are CLI tests (written as such) even if they should be testing the API or we want equivalent API tests. Actually testing the API directly will almost certainly look different from the current CLI tests and has its trade-offs. Couching CLI tests in the guise of API tests doesn't actually improve the tests but porting CLI tests to use the API under the hood does deliver the above benefits. Additionally, it seems to me that an ideal test suite would allow both 'low-level' API-style tests and 'high-level' CLI-style tests. As you note, some functionality like the plugins system greatly benefits from high-level tests.

It seems to me that any large scale improvement of the test suite should be done incrementally. Demanding that tests directly ported from integration-cli conform to some unwritten 'improved' specification seems counter-productive. The port itself (including functions that are 1:1 with the CLI) is valuable and speeds progress toward your long-term goals without demanding test rewrites by contributors or blocking other work. At a later stage, the CLI tests that have been ported can be rewritten or fixed up in uniform and well-defined stages as necessary.

@dsheets
Copy link
Copy Markdown
Contributor Author

dsheets commented Sep 28, 2017

The ideal way to test these plugins would really be to re-use the test suite for the actual component. If we had the tests annotated correctly we could spin up a daemon that has the plugin loaded and then trigger the component test suite against that daemon. With the right test requirements/skips setup we wouldn't have to write a second test suite. We would have just a couple extra test cases for the "disallowed by plugin" case, which would get skipped when run against a non-plugin daemon.

I don't believe that design is feasible as these plugin tests target the plugin system's functionality that extends beyond a single plugin or accept/reject behavior. Examine the tests for daemon start failure or the instrumentation of authz v1 plugins in this PR for evidence.

@dsheets
Copy link
Copy Markdown
Contributor Author

dsheets commented Sep 28, 2017

Test helpers which accept t *testing.T as the first argument instead of returning error are also really nice. If we keep some of these helpers I would suggest passing in testing.T, which will help keep the tests readable and not distract from the relevant lines in the test.

By not passing t *testing.T to the CLI analogs we retain the ability to test for expected failure.

@dsheets dsheets force-pushed the authz-tests-api-port branch 2 times, most recently from 810ebeb to 924c517 Compare October 2, 2017 13:19
David Sheets added 2 commits October 2, 2017 14:20
Signed-off-by: David Sheets <dsheets@docker.com>
I strongly disagree with the design of this pull request.

Signed-off-by: David Sheets <dsheets@docker.com>
@dsheets dsheets force-pushed the authz-tests-api-port branch from 924c517 to 1574d91 Compare October 2, 2017 13:21
@dsheets
Copy link
Copy Markdown
Contributor Author

dsheets commented Oct 2, 2017

I have rebased, squashed, and made the changes you requested. I strongly disagree with this design.

Please review and merge ASAP.

Also skip.IfCondition directly from the test, so that the skip message is correct

Signed-off-by: Daniel Nephin <dnephin@docker.com>
Copy link
Copy Markdown
Member

@dnephin dnephin left a comment

Choose a reason for hiding this comment

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

I pushed a small change to the integration path exclude list regex, and moved skip.IfCondition() into the test.

LGTM

@dsheets
Copy link
Copy Markdown
Contributor Author

dsheets commented Oct 5, 2017

So when can this be merged?

@dnephin
Copy link
Copy Markdown
Member

dnephin commented Oct 5, 2017

Needs another LGTM

Copy link
Copy Markdown
Member

@vdemeester vdemeester left a comment

Choose a reason for hiding this comment

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

LGTM 🐮

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants