Skip to content
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

Add support for multi-writer PD #415

Merged
merged 13 commits into from
Aug 12, 2020

Conversation

sschmitt
Copy link
Contributor

/kind feature

/cc @msau42

What this PR does / why we need it:
Adds multi writer support (currently alpha in GCP).

Special notes for your reviewer:
There's a bit of code duplication due to alpha copies of certain methods. I found it challenging to reduce the duplication but I'd be open to suggestions on how to refactor. On the other hand it might just be a temporary situation until the API's move to beta / GA.

Does this PR introduce a user-facing change?:

Add support for multi-writer raw block devices.

@k8s-ci-robot
Copy link
Contributor

Welcome @sschmitt!

It looks like this is your first PR to kubernetes-sigs/gcp-compute-persistent-disk-csi-driver 🎉. Please refer to our pull request process documentation to help your PR have a smooth ride to approval.

You will be prompted by a bot to use commands during the review process. Do not be afraid to follow the prompts! It is okay to experiment. Here is the bot commands documentation.

You can also check if kubernetes-sigs/gcp-compute-persistent-disk-csi-driver has its own contribution guidelines.

You may want to refer to our testing guide if you run into trouble with your tests not passing.

If you are having difficulty getting your pull request seen, please follow the recommended escalation practices. Also, for tips and tricks in the contribution process you may want to read the Kubernetes contributor cheat sheet. We want to make sure your contribution gets all the attention it needs!

Thank you, and welcome to Kubernetes. 😃

@k8s-ci-robot k8s-ci-robot added release-note Denotes a PR that will be considered when it comes time to generate release notes. kind/feature Categorizes issue or PR as related to a new feature. 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. needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. labels Oct 23, 2019
@k8s-ci-robot
Copy link
Contributor

Hi @sschmitt. Thanks for your PR.

I'm waiting for a kubernetes-sigs or kubernetes member to verify that this patch is reasonable to test. If it is, they should reply with /ok-to-test on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should join the org to skip this step.

Once the patch is verified, the new status will be reflected by the ok-to-test label.

I understand the commands that are listed here.

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.

@k8s-ci-robot k8s-ci-robot added the size/L Denotes a PR that changes 100-499 lines, ignoring generated files. label Oct 23, 2019
@msau42
Copy link
Contributor

msau42 commented Oct 23, 2019

/ok-to-test
/assign @davidz627

@k8s-ci-robot k8s-ci-robot added ok-to-test Indicates a non-member PR verified by an org member that is safe to test. and removed needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. labels Oct 23, 2019
@k8s-ci-robot k8s-ci-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Oct 24, 2019
@@ -288,6 +297,66 @@ func (cloud *CloudProvider) insertRegionalDisk(ctx context.Context, volKey *meta
return nil
}

func (cloud *CloudProvider) insertRegionalAlphaDisk(ctx context.Context, volKey *meta.Key, diskType string, capBytes int64, capacityRange *csi.CapacityRange, replicaZones []string, snapshotID, diskEncryptionKmsKey string, multiWriter bool) error {
diskToCreateAlpha := &computealpha.Disk{
Copy link
Contributor

@davidz627 davidz627 Oct 24, 2019

Choose a reason for hiding this comment

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

Thanks for doing this, It looks pretty good as-is but I think there's also a way to de-dupe some of this copy-pasted code.

Assuming zonal here:

  1. Have one cloud.insertDisk that takes in all the parameters
  2. Create a v1 disk with logic up to line 318 here.
  3. Make insertOp a interface{} type (this part is kind of nasty, suggestions welcome)
if multiWriter { 
  alphadisk := convertV1DiskToV1AlphaDisk(disk) // you have to write this
  insertOp = cloud.alphaService.Insert(alphadisk)
} else{
  insertOp = cloud.service.Insert(disk)
}

Then the rest of the error handling logic is shared
4) typeAssert on insertOp and call the respective waitForOp on it
5) Share the other logic

This would dedupe most of the code and only have branching for the actual GCE API Insert call and the WaitForOp.
Let me know what you think. Happy to discuss more

/cc @msau42 @misterikkit

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the review. I notice the waitForOp methods only refer to op.Name. I wonder if it's okay to call the non-alpha operations API to check on the status of an alpha operation. My thinking there is that we're not leveraging alpha features of the operations API.
I'm not sure so I'll err on the side of caution here. The solution you recommended works just fine.

Copy link
Contributor

Choose a reason for hiding this comment

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

ack. I didn't notice that - we could just grab the op name then and avoid the whole type assertion thing

Copy link
Contributor

Choose a reason for hiding this comment

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

wait... looked at it again and we still might need to since we're doing the op get on svc.ZoneOperations.Get(project, zone, op.Name).Context(ctx).Do() - thats the v1 service. If we have an alpha service op maybe we can't "find it" unless we use a v1alpha.ZoneOperations call? Could you verify

Copy link
Contributor Author

Choose a reason for hiding this comment

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

David, I ran some tests and found that the v1 operations API is able to get the status of beta and alpha operations.

This can easily be tested using the in-browser API Explorer:
https://cloud.google.com/compute/docs/reference/rest/beta/disks/insert
https://cloud.google.com/compute/docs/reference/rest/v1/zoneOperations/get

I think there also might be some opportunity to reduce the duplication between Zonal and Regional methods but I'll skip that for now. You can let me know your thoughts there. I'm happy to make additional changes.

In the meantime I'll address the rest of your comments and add another commit.

Copy link
Contributor

Choose a reason for hiding this comment

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

lets keep this change focused on adding the multiwriter stuff - feel free to open up a fix to reduce zonal+regional duplication afterwards if you're interested. That would be really cool 👍

Ack on the ops - lets dedupe them and make sure to add a comment in the code with that exact finding (so someone doesn't come in and waste cycles trying to "fix" it later)

Copy link
Contributor

@davidz627 davidz627 left a comment

Choose a reason for hiding this comment

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

Mostly looks good, just some comments about code de-dupe and maybe some missed pieces.

Could you please write some E2E tests for this too? You can see examples in:
test/e2e/tests/single_zone_e2e_test.go
and run them locally with:
test/run-e2e-local.sh

insertOp, err := cloud.alphaService.RegionDisks.Insert(cloud.project, volKey.Region, diskToCreateAlpha).Context(ctx).Do()
if err != nil {
if IsGCEError(err, "alreadyExists") {
disk, err := cloud.GetDisk(ctx, volKey)
Copy link
Contributor

Choose a reason for hiding this comment

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

does GetDisk need to be changed as well? If we are to get a disk that is multi-writer what happens (since it is only on the alpha struct)?

Copy link
Contributor

Choose a reason for hiding this comment

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

you may be able to leverage CloudDisk here (originally created for this different api problem for RePD)

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, GetDisk has to be updated. CloudDisk looks to be the way to go.

if err != nil {
return err
}
err = cloud.ValidateExistingDisk(ctx, disk, diskType,
Copy link
Contributor

Choose a reason for hiding this comment

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

validation might also need to be changed to check if multiWriter field is equal

@@ -346,14 +346,19 @@ func TestCreateVolumeArguments(t *testing.T) {
},
},
{
name: "fail with MULTI_NODE_MULTI_WRITER capability",
name: "success with block/MULTI_NODE_MULTI_WRITER capabilities",
Copy link
Contributor

Choose a reason for hiding this comment

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

what about fs/MULTI_NODE_MULTI_WRITER should we fail?

Copy link
Contributor

Choose a reason for hiding this comment

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

@nikhilkathare Any details here?

Copy link
Contributor

@nikhilkathare nikhilkathare Jul 20, 2020

Choose a reason for hiding this comment

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

@mattcary : Hi matt, fs/MULTI_NODE_MULTI_WRITER has been handled above as mount/MULTI_NODE_MULTI_WRITER capability.

@msau42
Copy link
Contributor

msau42 commented Oct 24, 2019

@davidz627 I think the e2e is going to require the boskos projects to be whitelisted before we can merge it

@sschmitt
Copy link
Contributor Author

Mostly looks good, just some comments about code de-dupe and maybe some missed pieces.

Could you please write some E2E tests for this too? You can see examples in:
test/e2e/tests/single_zone_e2e_test.go
and run them locally with:
test/run-e2e-local.sh

@davidz627 E2E is a little tricky because this feature is still in Alpha. I also notice that the E2E tests are hardcoded for us-central1. Multi-writer PD is currently only available in us-east1-a in whitelisted projects.

Should I write E2E tests and leave them commented out or skip them?

@davidz627
Copy link
Contributor

davidz627 commented Oct 29, 2019

I also notice that the E2E tests are hardcoded for us-central1

This could probably be changed pretty easily - please do so unless it seems like it would be significant additional investment.

in whitelisted projects.

I will take a look at how to resolve this and try get the projects all whitelisted - I will update this PR when I know more.

Should I write E2E tests and leave them commented out or skip them?

Yes, have them skipped for now. However, you should be able to run the tests in your own whitelisted project[s]

Thanks!

@sschmitt
Copy link
Contributor Author

@davidz627 Perfect. Thanks for the guidance.

@k8s-ci-robot k8s-ci-robot added size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. and removed size/L Denotes a PR that changes 100-499 lines, ignoring generated files. labels Nov 4, 2019
@@ -182,15 +182,22 @@ func (cloud *FakeCloudProvider) ValidateExistingDisk(ctx context.Context, resp *
return fmt.Errorf("disk already exists with incompatible type. Need %v. Got %v",
diskType, respType[len(respType)-1])
}

// We are assuming here that a multiWriter disk could be used as non-multiWriter
if multiWriter && !resp.GetMultiWriter() {
Copy link
Contributor

Choose a reason for hiding this comment

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

how about the other way around

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The other way around would be when the existing disk is enabled for multi-writer but the user didn't ask for that capability. I assume here that the user would be okay with that.

It's actually quite challenging to check the opposite because the user might not have Alpha API access. I suppose we could first try alpha and if that fails fall back to v1.

@@ -298,7 +305,7 @@ func (gceCS *GCEControllerServer) ControllerPublishVolume(ctx context.Context, r
PublishContext: nil,
}

_, err = gceCS.CloudProvider.GetDisk(ctx, volKey)
_, err = gceCS.CloudProvider.GetDisk(ctx, volKey, gce.V1)
Copy link
Contributor

Choose a reason for hiding this comment

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

why is this definitely V1? Couldn't it be a alpha multiwriter disk that we want to get here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The v1 API returns disks that use alpha features, it just doesn't have any information with respect to those features. Here the API call is used to detect the existence of the disk. The disk itself is thrown away and only the error code is parsed. I didn't see a need to use anything other than v1.

alphaDiskToCreate := convertV1DiskToAlphaDisk(diskToCreate)
alphaDiskToCreate.MultiWriter = multiWriter
insertOp, err = cloud.alphaService.RegionDisks.Insert(cloud.project, volKey.Region, alphaDiskToCreate).Context(ctx).Do()
if err == nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: if insertOp != nil instead?

var (
err error
opName string
apiVersion = V1
Copy link
Contributor

Choose a reason for hiding this comment

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

Lets call this gceAPIVersion to disambiguate.

@sschmitt
Copy link
Contributor Author

@davidz627 Are there any further concerns? You had a question here, I wasn't sure if you saw my response.

@@ -346,14 +346,19 @@ func TestCreateVolumeArguments(t *testing.T) {
},
},
{
name: "fail with MULTI_NODE_MULTI_WRITER capability",
name: "success with block/MULTI_NODE_MULTI_WRITER capabilities",
Copy link
Contributor

Choose a reason for hiding this comment

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

@nikhilkathare Any details here?

@@ -257,7 +257,11 @@ func testLifecycleWithVerify(volID string, volName string, instance *remote.Inst
if secondMountVerify != nil {
// Mount disk somewhere else
secondPublishDir := filepath.Join("/tmp/", volName, "secondmount")
Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry if I'm missing something from the test setup, but will this test multizonal shared PD?

Copy link
Contributor

Choose a reason for hiding this comment

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

Shared PD is yet not supported on multizone. This is the fix added to handle function testLifecycleWithVerify with useBlock=true.

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for clarifying.

@nikhilkathare
Copy link
Contributor

/assign @mattcary

@mattcary
Copy link
Contributor

Thanks for resurrecting this PR, Nikhil. It looks good to me, just a couple of general questions:

  1. the multiwriter PD API is beta now. But I think continuing to work here through the alpha API makes sense in case there are new alpha-only features that we end up having to use. But maybe not, WDYT?

  2. I believe that in beta only scsi is supported by PD, but by GA it will be NVMe only. Do you see that causing any problems?

@nikhilkathare
Copy link
Contributor

Hi Matt, Thanks for your review.
Response for your questions.

  1. Agreed. Let us go ahead and use Alpha APIs for now. Beta APIs changes can be done later as required.
  2. We don’t see any changes required for NVMe but if required we can make the necessary changes after testing on NVMe PDs whenever it becomes available.

@mattcary
Copy link
Contributor

Hi Matt, Thanks for your review.
Response for your questions.

  1. Agreed. Let us go ahead and use Alpha APIs for now. Beta APIs changes can be done later as required.
  2. We don’t see any changes required for NVMe but if required we can make the necessary changes after testing on NVMe PDs whenever it becomes available.

Cool, thanks for the info.

@mattcary
Copy link
Contributor

/lgtm

@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Jul 29, 2020
@nikhilkathare
Copy link
Contributor

/cc @msau42 @mattcary We are not clear if anything is pending from our side. Can we know when will this PR be approved and merged ?

@msau42
Copy link
Contributor

msau42 commented Aug 6, 2020

@nikhilkathare this lgtm! Thanks for picking this up! One last thing, would you be able to squash the commits to reduce some of the "merge branch" and "address comments" commits? If you haven't tried it before, I would suggest practicing in another branch before doing it here. Take a look here for pointers on how to do the squshing.

@k8s-ci-robot k8s-ci-robot added needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. and removed lgtm "Looks good to me", indicates that a PR is ready to be merged. labels Aug 9, 2020
@k8s-ci-robot k8s-ci-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Aug 9, 2020
@nikhilkathare
Copy link
Contributor

/cc @msau42 @mattcary Thanks for reviewing the code. Tried to squash the commits using rebase but I could squash only final commits(last 5 commits) which were done by me. When tried to squash further, rebase is erroring out with conflicts while applying commits and exiting to resolve errors manually in other commits. As commits in this PR are done with lot of gap there are many commits that have got in between which squash is not able to resolve. Should we create new branch and take change from this branch to new branch or any other better approach. Let us know what approach you would like us to take ?

@msau42
Copy link
Contributor

msau42 commented Aug 10, 2020

Hi @nikhilkathare can you try just squashing your own commits then? It's not a big deal if we can't get it to work, it's mainly to just try to cleanup the commit listing a little bit.

@nikhilkathare
Copy link
Contributor

@msau42 Thanks for quick response. Yes I have squashed my five changes to one and pushed, this led to removal of tag lgtm from PR. Now no further squash could be done as there is merge(which has other commits) between our PR commits.

@msau42
Copy link
Contributor

msau42 commented Aug 11, 2020

/lgtm
/approve

Thank you so much!

@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Aug 11, 2020
@k8s-ci-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: msau42, sschmitt

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 /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@k8s-ci-robot k8s-ci-robot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Aug 11, 2020
@msau42
Copy link
Contributor

msau42 commented Aug 11, 2020

/retest

2 similar comments
@msau42
Copy link
Contributor

msau42 commented Aug 11, 2020

/retest

@msau42
Copy link
Contributor

msau42 commented Aug 12, 2020

/retest

@k8s-ci-robot k8s-ci-robot merged commit 58011dc into kubernetes-sigs:master Aug 12, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. kind/feature Categorizes issue or PR as related to a new feature. lgtm "Looks good to me", indicates that a PR is ready to be merged. ok-to-test Indicates a non-member PR verified by an org member that is safe to test. release-note Denotes a PR that will be considered when it comes time to generate release notes. size/XL Denotes a PR that changes 500-999 lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants