New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
e2e/storage: delay CreateDriver, simplify APIs and test definition #72434
e2e/storage: delay CreateDriver, simplify APIs and test definition #72434
Conversation
/cc @mkimuram |
@pohly: GitHub didn't allow me to request PR reviews from the following users: mkimuram. Note that only kubernetes members and repo collaborators can review this PR, and authors cannot review their own PRs. In response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. |
This WIP because only the provisioning testsuite has been updated. To get the code compiled, the rest of the testsuites were removed. |
I'd like to get feedback on this approach and a decision on #70992 before continuing with this PR. |
1d427cf
to
c764180
Compare
I understand that 12th commit is the core part of this PR, 1st-5th commits are from #70992, and 6th-11th commit are doing other minor fixes or improvements than separating config from driver. I think that it would be easier to be reviewed by separating 6th-11th from this PR if they aren't prerequisites for this PR. As for 12th commit, I agree to separate config from driver. Can this refactoring be easily applied to other testsuites? If there are easy way to achieve it, I agree to change this way, for it looks easier to read and understand. |
/cc |
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.
just a quick first pass. Thanks for working on this!
@@ -59,7 +59,7 @@ type TestVolume interface { | |||
type PreprovisionedVolumeTestDriver interface { | |||
TestDriver | |||
// CreateVolume creates a pre-provisioned volume of the desired volume type. | |||
CreateVolume(volumeType testpatterns.TestVolType) TestVolume | |||
CreateVolume(config *TestConfig, volumeType testpatterns.TestVolType) TestVolume |
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.
same as with CreateDriver, I think it would be clearer to return a TestConfig if CreateVolume may modify the config
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.
So you meant it should take a TestConfig
and return a potentially different one?
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
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.
Not done yet.
c764180
to
4410815
Compare
@msau42 @wongma7 @davidz627: all tests green (including gce alpha and serial), no further changes planned as far as I am concerned -> please have a look. |
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 just had some inconsequential suggestions.
driver & driver interface changes lgtm.
testsuite changes largely lgtm, may need to do another pass. Agree with removing skipUnsupportedTest from testsuites, only snapshots implemented it
// Now do the more expensive test initialization. | ||
config, testCleanup = driver.PrepareTest(f) | ||
resource = createGenericVolumeTestResource(driver, config, pattern) | ||
if resource.volSource == nil { |
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.
nit, check not needed
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.
Probably not, but it was there in the original code and I wanted to avoid changing test logic.
// Now do the more expensive test initialization. | ||
config, testCleanup = driver.PrepareTest(f) | ||
resource = createGenericVolumeTestResource(driver, config, pattern) | ||
if resource.volSource == nil { |
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.
same
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.
Same 🤷♂️. I wanted to avoid changing test logic.
claimSize := dDriver.GetClaimSize() | ||
sc = dDriver.GetDynamicProvisionStorageClass(config, "") | ||
if sc == nil { | ||
framework.Skipf("Driver %q does not define Dynamic Provision StorageClass - skipping", dInfo.Name) |
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.
these Skip messages should specify that the sc is not defined because the fstype %s is not supported. there is no other reason (currently?) for a driver to implement DynamicPVTestDriver but return nil
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.
Can this code here know why the driver didn't return a storage class? I don't think it should make assumptions based on the current implementation.
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.
yeah I guess it's fine as-is. I guess it is the driver author's responsibility to make sense of why their GetDynamicProvisionStorageClass would return nil. Maybe it could return an error in the future, seems pointless now though
// TestDynamicProvisioning tests dynamic provisioning with specified StorageClassTest | ||
func (t StorageClassTest) TestDynamicProvisioning() *v1.PersistentVolume { | ||
client := t.Client | ||
claim := t.Claim |
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.
could fail w/ nice error if this is nil
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.
Not sure if Github is showing me the right context. What can be nil here? t.Client
and/or t.Claim
?
Are you worried that they might be accidentally passed as nil because they are now struct members instead of separate function parameters? I agree, this isn't caught explicitly. If this is the only requested change, then I'd prefer to add assertions in a separate 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.
I meant t.Claim, though client could be nil as well. either way, the nil error will be caught a few lines below, so yeah these assertions can be added in a separate PR as "nice-to-haves" 👍
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.
As I was rebasing anyway, I added the asserts.
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 can't speak to the full PR, but the parts I'm familiar with LGTM.
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.
mostly LGTM. Just a couple small comments. Thanks for doing this!
test/e2e/storage/csi_volumes.go
Outdated
defer destroyCSIDriver(csics, driver) | ||
defer driver.CleanupDriver() | ||
defer destroyCSIDriver(csics, config.GetUniqueDriverName()) | ||
defer testCleanup() |
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.
shouldn't we want to cleanup the test whether the driver exists or not
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.
By("Deleting pod") | ||
err := framework.DeletePodWithWait(f, cs, pod) | ||
Expect(err).ToNot(HaveOccurred(), "while deleting pod") | ||
pod = nil |
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 is suspicious to me. I want to avoid a pattern where we are reusing structs and just clearing field. Is there a way we can start from scratch instead?
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.
The test does start from scratch, with a fresh copy of all local variables. Setting the pointers to nil is meant to ensure that cleanup
can be called more than once, should the need ever arise. Currently this isn't done, so I could remove the pod = nil
and leave the pointers to already deleted items, but personally would prefer to keep this extra precaution.
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.
Hmm, I need to be more precise: each test starts with a fresh copy of the the local variables if init
initializes all of them. I see how that might be something that can be missed for a complex test suite where some variables are only set under certain conditions.
This can be avoided by storing all these local variables in a struct and re-initializing the struct before setting individual members. I've done that in commit ec3655a.
I'm undecided whether it makes the code easier to read ("shared" variables clearly marked with l.
prefix) or harder to read (extra prefix). @davidz627 Please let me know whether we should do it this way or by carefully setting all variables in init.
This increases type safety and makes the code easier to read because it becomes obvious that the "test resource" passed to some functions must be the result of a previous CreateVolume. This makes it possible to remove: - functions that never did anything (the DeleteVolume methods in drivers that never create a volume) - type casts (in the DeleteVolume implementation) - the unused DeleteVolume parameters - the stand-alone DeleteVolume function (which would be just a non-nil check) GetPersistentVolumeSource and GetVolumeSource could also become methods on more specific interfaces - they don't actually use anything from TestDriver instance which provides them. The main motivation however is to reduce the number of methods which might need an explicit test config parameter.
CreateDriver (now called SetupTest) is a potentially expensive operation, depending on the driver. Creating and tearing down a framework instance also takes time (measured at 6 seconds on a fast machine) and produces quite a bit of log output. Both can be avoided for tests that skip based on static information (like for instance the current OS, vendor, driver and test pattern) by making the test suite responsible for creating framework and driver. The lifecycle of the TestConfig instance was confusing because it was stored inside the DriverInfo, a struct which conceptually is static, while the TestConfig is dynamic. It is cleaner to separate the two, even if that means that an additional pointer must be passed into some functions. Now CreateDriver is responsible for initializing the PerTestConfig that is to be used by the test. To make this approach simpler to implement (= less functions which need the pointer) and the tests easier to read, the entire setup and test definition is now contained in a single function. This is how it is normally done in Ginkgo. This is easier to read because one can see at a glance where variables are set, instead of having to trace values though two additional structs (TestResource and TestInput). Because we are changing the API already, also other changes are made: - some function prototypes get simplified - the naming of functions is changed to match their purpose (tests aren't executed by the test suite, they only get defined for later execution) - unused methods get removed (TestSuite.skipUnsupportedTest is redundant)
The recommended approach for not running unsuitable tests is to skip them at runtime with an explanation. Filtering out unsuitable test patters and thus not even defining unsuitable tests was done earlier because it was faster than skipping tests at runtime. But now these tests can be skipped efficiently, so this special case can be removed.
Generated via hack/update-bazel.sh.
There is a risk that the init function does not reset one of the local variables that was set by a previous test. To avoid this, all variables set by init are now in a struct which gets cleaned completely first.
1692255
to
ec3655a
Compare
/test pull-kubernetes-e2e-gce-alpha-features |
/priority important-soon |
@childsb this should be 1.14 milestone right? /approve will let @davidz627 and/or @wongma7 lgtm |
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: msau42, pohly The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
/lgtm |
/retest Review the full test history for this PR. Silence the bot with an |
1 similar comment
/retest Review the full test history for this PR. Silence the bot with an |
What type of PR is this?
/kind cleanup
What this PR does / why we need it:
CreateDriver is a potentially expensive operation, depending on the
driver. Creating and tearing down a framework instance also takes
time (measured at 6 seconds on a fast machine) and produces quite a
bit of log output.
Both can be avoided for tests that skipped based on static
information (like for instance the current OS, vendor, driver and test
pattern) by making the test suite responsible for creating framework
and driver.
The lifecycle of the TestConfig instance was confusing because it was
stored inside the DriverInfo, a struct which conceptually is static,
while the TestConfig is dynamic. It is cleaner to separate the two,
even if that means that an additional pointer must be passed into some
functions. Now CreateDriver is responsible for initializing the
PerTestConfig that is to be used by the test.
To make this approach simpler to implement (= less functions which
need the pointer) and the tests easier to read, the entire setup and
test definition is now contained in a single function. This is how it
is normally done in Ginkgo. This is easier to read because one can see
at a glance where variables are set, instead of having to trace values
though two additional structs (TestResource and TestInput).
Because we are changing the API already, also other changes are made:
(tests aren't executed by the test suite, they only get defined
for later execution)
Which issue(s) this PR fixes:
Fixes #72288
Special notes for your reviewer:
Does this PR introduce a user-facing change?: