Skip to content

Add PVC spec mutating webhook rendering based on StorageProfiles - #2813

Merged
kubevirt-bot merged 5 commits into
kubevirt:mainfrom
arnongilboa:pvc_mutate_experiment
Feb 12, 2024
Merged

kubevirt-bot merged 5 commits into
kubevirt:mainfrom
arnongilboa:pvc_mutate_experiment

Conversation

@arnongilboa

@arnongilboa arnongilboa commented Jul 19, 2023 •

Copy link
Copy Markdown
Collaborator

What this PR does / why we need it:

The webhook mutates the PVC Spec based on the available StorageProfiles, so for example you can create PVC without accessModes and it will be auto-completed.

To use this feature, enable the WebhookPvcRendering feature gate.

For any PVC you want to use StorageProfile, label it with cdi.kubevirt.io/useStorageProfile: "true"

If you want to use volumeMode preferred by CDI according to StorageProfiles, set it to FromStorageProfile. Otherwise if not explicitly set to Block, it will be Filesystem by k8s default.

E.g.:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-test
  labels:
    cdi.kubevirt.io/useStorageProfile: "true"
spec:
  storageClassName: rook-ceph-block
  volumeMode: FromStorageProfile
  resources:
    requests:
      storage: 1Mi

Special notes for your reviewer:

Release note:

Add PVC spec mutating webhook rendering based on StorageProfiles

@kubevirt-bot kubevirt-bot added release-note Denotes a PR that will be considered when it comes time to generate release notes. do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. dco-signoff: yes Indicates the PR's author has DCO signed all their commits. size/XL labels Jul 19, 2023
@kubevirt-bot
kubevirt-bot requested review from alromeros and awels July 19, 2023 10:16
@arnongilboa
arnongilboa force-pushed the pvc_mutate_experiment branch from a78691e to 9453e09 Compare July 19, 2023 10:23
@arnongilboa arnongilboa changed the title [wip] Add PVC Spec mutating webhook rendering based on the available StorageProfiles [wip] Add PVC spec mutating webhook rendering based on the available StorageProfiles Jul 19, 2023
@arnongilboa arnongilboa changed the title [wip] Add PVC spec mutating webhook rendering based on the available StorageProfiles [wip] Add PVC spec mutating webhook rendering based on StorageProfiles Jul 19, 2023
@awels

awels commented Jul 19, 2023

Copy link
Copy Markdown
Member

So is there any particular reason, we can't fill out partially completed PVC specs, so if I want to override the storage profile (which may be block) to filesystem, I just fill out the volumeMode in the pvc spec, and anything missing is completed by the webhook? This would mirror what we are doing with DataVolumes right now.

I like that it only applies if you annotate the PVC, so we won't be doing it for all PVCs.

@arnongilboa

Copy link
Copy Markdown
Collaborator Author

So is there any particular reason, we can't fill out partially completed PVC specs, so if I want to override the storage profile (which may be block) to filesystem, I just fill out the volumeMode in the pvc spec, and anything missing is completed by the webhook? This would mirror what we are doing with DataVolumes right now.

That's exactly what we are doing in this POC. Copied the DV StorageProfiles logic to the webhook and patched it, just to get it quickly working.

I like that it only applies if you annotate the PVC, so we won't be doing it for all PVCs.

Right

@awels

awels commented Jul 19, 2023

Copy link
Copy Markdown
Member

So what is the point of cdi.kubervirt.io/useStorageProfile: "Filesystem" then? You can just set the volumeMode to filesystem.

@arnongilboa

Copy link
Copy Markdown
Collaborator Author

So what is the point of cdi.kubervirt.io/useStorageProfile: "Filesystem" then? You can just set the volumeMode to filesystem.

The k8s PVC mutating webhook puts the default Filesystem (if spec.volumeMode is nil) before it gets to CDI PVC mutating webhook, so we can't know if it's the user or k8s choice when we get it.

@akalenyu

akalenyu commented Jul 20, 2023 •

Copy link
Copy Markdown
Collaborator

So what is the point of cdi.kubervirt.io/useStorageProfile: "Filesystem" then? You can just set the volumeMode to filesystem.

The k8s PVC mutating webhook puts the default Filesystem (if spec.volumeMode is nil) before it gets to CDI PVC mutating webhook, so we can't know if it's the user or k8s choice when we get it.

This may help:
https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#reinvocation-policy

Also pasting the object selector link from our offline discussion:
https://github.com/kubevirt/kubevirt/blob/main/tests/storage/snapshot.go#L183

@arnongilboa
arnongilboa force-pushed the pvc_mutate_experiment branch from 9453e09 to b9d81c5 Compare July 20, 2023 15:42
@arnongilboa

Copy link
Copy Markdown
Collaborator Author

/retest

@arnongilboa
arnongilboa force-pushed the pvc_mutate_experiment branch from b9d81c5 to 071f54b Compare July 20, 2023 16:09
@arnongilboa
arnongilboa force-pushed the pvc_mutate_experiment branch from 071f54b to e8a3988 Compare July 30, 2023 08:24
@arnongilboa
arnongilboa force-pushed the pvc_mutate_experiment branch 5 times, most recently from e2e64d2 to 7ae2147 Compare August 30, 2023 15:44
Comment thread pkg/apiserver/webhooks/pvc-mutate.go Outdated
}

func (wh *pvcMutatingWebhook) ListPersistentVolumes(ctx context.Context) (*v1.PersistentVolumeList, error) {
return wh.k8sClient.CoreV1().PersistentVolumes().List(ctx, metav1.ListOptions{})

@akalenyu akalenyu Aug 30, 2023 •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Note these are not cached clients so those list calls will be costly at scale ("real" clusters)

@arnongilboa arnongilboa Aug 30, 2023 •

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Rendering is done once per labeled-pvc creation, so not sure it's that expansive in comparison to its population...

Do we have a way to use the cached client from the webhook?

@arnongilboa
arnongilboa force-pushed the pvc_mutate_experiment branch 3 times, most recently from ebdd48f to e475ecf Compare August 31, 2023 07:42
@arnongilboa arnongilboa changed the title [wip] Add PVC spec mutating webhook rendering based on StorageProfiles [WIP] Add PVC spec mutating webhook rendering based on StorageProfiles Aug 31, 2023
@arnongilboa
arnongilboa force-pushed the pvc_mutate_experiment branch 2 times, most recently from 98d7c18 to ebdd48f Compare August 31, 2023 11:11
@arnongilboa

Copy link
Copy Markdown
Collaborator Author

/retest

1 similar comment
@arnongilboa

Copy link
Copy Markdown
Collaborator Author

/retest

@kubevirt-bot kubevirt-bot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Jan 23, 2024
@arnongilboa
arnongilboa force-pushed the pvc_mutate_experiment branch from b04b51f to 10f670a Compare January 25, 2024 13:24
@kubevirt-bot kubevirt-bot added size/XXL and removed needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. size/XL labels Jan 25, 2024
@arnongilboa
arnongilboa force-pushed the pvc_mutate_experiment branch 3 times, most recently from 7147ef1 to 08c9e4a Compare January 25, 2024 14:36
The webhook mutates the PVC Spec based on the available StorageProfiles,
so for example you can create PVC without accessModes and it will be
auto-completed.

To use this feature, enable the `WebhookPvcRendering` feature gate.

For any PVC you want to use StorageProfile, label it with:
    cdi.kubevirt.io/useStorageProfile: "true"

If you want to use volumeMode preferred by CDI according to
StorageProfiles, set it to FromStorageProfile. Otherwise if not
explicitly set to Block, it will be Filesystem by k8s default.

E.g.:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-test
  labels:
    cdi.kubevirt.io/useStorageProfile: "true"
spec:
  storageClassName: rook-ceph-block
  volumeMode: FromStorageProfile
  resources:
    requests:
      storage: 1Mi

Signed-off-by: Arnon Gilboa <[email protected]>
}
return nil
}
if err := c.Delete(context.TODO(), whc); client.IgnoreNotFound(err) != nil {

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.

pkg/operator/resources is really just for static configuration. Check out callbacks.go. You can write a callback that creates/deletes the webhook based on the feature gate somewhat similar to what we do when creating Routes/SCCs on openshift

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

fixed

)

// RenderPvc renders the PVC according to StorageProfiles
func RenderPvc(ctx context.Context, client client.Client, pvc *v1.PersistentVolumeClaim) error {

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.

What about setting virt default storage class if storageclass is unset?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

We get it for free from the call to renderPvcSpecVolumeModeAndAccessModes() which uses GetStorageClassByNameWithVirtFallback().

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.

Okay do you think it would make sense to extract that bit and create a renderPvcSpecStorageClass function? Or change renderPvcSpecVolumeModeAndAccessModes to renderPvcSpecVolumeModeAndAccessModesAndStorageClass?

Comment thread pkg/operator/controller/callbacks.go Outdated
return nil
}

crd := args.CurrentObject.(*extv1.CustomResourceDefinition)

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 it would make more sense to link this with the "cdi-apiserver" deployment. If for some reason cdi-apiserver is not created we would not want to create a webhook

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

But then how would you delete the webhook if "cdi-apiserver" somehow disappears? not sure these cases are relevant as you cannot do anything without "cdi-apiserver" (can you?), and we reconcile for its eventual consistency.

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.

Yeah interesting point, it is doubtful that we will ever not deploy cdi-apiserver but I still think it is better to time the creation of the webhook close to the creation of cdi-apiserver ideally right before it

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

fixed

Comment thread pkg/operator/controller/callbacks.go Outdated
return err
}
if enabled {
whc = cluster.CreatePvcMutatingWebhook(args.Namespace, args.Client, args.Logger)

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 to be consistent with the other resources created by callbacks, maybe put CreatePvcMutatingWebhook in this 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.

And don't forget to make sure it is owned by CDI

@arnongilboa arnongilboa Jan 31, 2024 •

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Move it to this package although all other similar webhook CR creation code is in the cluster package? much harder to maintain

@arnongilboa arnongilboa Jan 31, 2024 •

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

And don't forget to make sure it is owned by CDI

fixed

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.

Move it to this package although all other similar webhook CR creation code is in the cluster package? much harder to maintain

Can you help me understand that? To me, being in the same file or package is more maintainable for non-shared code

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Moved. I buy it when you explain it this way.

@awels awels left a comment

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 pretty good to me, a few nits.

Comment thread doc/pvc-mutating-webhook-rendering.md Outdated

PVC Mutating Webhook Rendering is an optional CDI feature, allowing users to get CDI PVC rendering functionality without using a `DataVolume`. Traditionally, when the CDI DV controller creates a PVC, it renders the PVC spec (`volumeMode`, `accessMode`, `storage`) according to the DV storage spec (or default) `storageClass`, CDI `StorageProfiles`, `CDIConfig` `filesystemOverhead` etc.

The PVC mutating webhook eliminats the need for DVs for `StorageProfile` based rendering, providing auto-completion of PVC missing spec fields based on optimal values per `StorageClass`. The webhook intercepts only explicitly CDI-labeled PVCs, so it won't affect cluster stability if the CDI api server is down. For labeled PVCs, `objectSelectors` decide when to call out over HTTP to the webhook, so if the CDI api server is down the request and PVC creation will fail. Unlabeled PVC will not be affected at all.

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.

Suggested change
The PVC mutating webhook eliminats the need for DVs for `StorageProfile` based rendering, providing auto-completion of PVC missing spec fields based on optimal values per `StorageClass`. The webhook intercepts only explicitly CDI-labeled PVCs, so it won't affect cluster stability if the CDI api server is down. For labeled PVCs, `objectSelectors` decide when to call out over HTTP to the webhook, so if the CDI api server is down the request and PVC creation will fail. Unlabeled PVC will not be affected at all.
The PVC mutating webhook eliminates the need for DVs for `StorageProfile` based rendering, providing auto-completion of PVC missing spec fields, based on optimal values per `StorageClass`. The webhook intercepts only explicitly CDI-labeled PVCs, so it won't affect cluster stability if the CDI api server is down. For labeled PVCs, `objectSelectors` decide when to call out over HTTP to the webhook, so if the CDI api server is down the request and PVC creation will fail. Unlabeled PVC will not be affected at all.

Comment thread pkg/controller/datavolume/util.go Outdated
}
volumeMode := v1.PersistentVolumeFilesystem
pvcSpec.VolumeMode = &volumeMode
pvcSpec.VolumeMode = &cc.FilesystemMode

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 we use ptr.To[type](volumeMode) here? To match its usage everywhere else?

plus some CR fixes and cleanups

Signed-off-by: Arnon Gilboa <[email protected]>
Comment thread pkg/operator/controller/callbacks.go Outdated
}

deployment, ok := args.DesiredObject.(*appsv1.Deployment)
if !ok || deployment.Name != "cdi-apiserver" {

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.

check out recently added CDIApiServerResourceName const

@arnongilboa

Copy link
Copy Markdown
Collaborator Author

/retest

@awels

awels commented Feb 1, 2024

Copy link
Copy Markdown
Member

/lgtm

Comment thread pkg/operator/controller/callbacks.go Outdated
},
}

if c == nil {

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 should never be the case in this context

err = args.Client.Get(context.TODO(), key, whc)
if err == nil {
if enabled {
return nil

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't exit here. May have to update the webhook configuration when:

  1. user changes any "critical" value
  2. but perhaps most importantly, whenever the cacert gets rotated the cabundle will change

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

fixed

return err
}
if !reflect.DeepEqual(whc, whcCopy) {
return args.Client.Update(context.TODO(), whc)

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 curious how frequently update would get called so I pulled your branch added a log here and ran the code. Turns out it was being called every reconcile even if nothing changed. Turned out it was because you are setting:

whc.APIVersion = "admissionregistration.k8s.io/v1"
whc.Kind = "MutatingWebhookConfiguration"

Controller runtime strips that out. So I'd suggest leaving it out here too.

FYI: it is added in operator/resources because the objects may be serialized to yaml

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Great catch & great lesson learned. Fixed.

@mhenriks

mhenriks commented Feb 9, 2024

Copy link
Copy Markdown
Member

/approve

@kubevirt-bot

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: mhenriks

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details 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

@awels

awels commented Feb 12, 2024

Copy link
Copy Markdown
Member

/lgtm

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. dco-signoff: yes Indicates the PR's author has DCO signed all their commits. lgtm Indicates that a PR is ready to be merged. release-note Denotes a PR that will be considered when it comes time to generate release notes. size/XXL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants