Repository navigation
Add PVC spec mutating webhook rendering based on StorageProfiles - #2813
Conversation
a78691e to
9453e09
Compare
|
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. |
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.
Right |
|
So what is the point of |
The k8s PVC mutating webhook puts the default |
This may help: Also pasting the object selector link from our offline discussion: |
9453e09 to
b9d81c5
Compare
|
/retest |
b9d81c5 to
071f54b
Compare
071f54b to
e8a3988
Compare
e2e64d2 to
7ae2147
Compare
| } | ||
|
|
||
| func (wh *pvcMutatingWebhook) ListPersistentVolumes(ctx context.Context) (*v1.PersistentVolumeList, error) { | ||
| return wh.k8sClient.CoreV1().PersistentVolumes().List(ctx, metav1.ListOptions{}) |
There was a problem hiding this comment.
Note these are not cached clients so those list calls will be costly at scale ("real" clusters)
There was a problem hiding this comment.
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?
ebdd48f to
e475ecf
Compare
98d7c18 to
ebdd48f
Compare
|
/retest |
1 similar comment
|
/retest |
b04b51f to
10f670a
Compare
7147ef1 to
08c9e4a
Compare
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 { |
There was a problem hiding this comment.
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
| ) | ||
|
|
||
| // RenderPvc renders the PVC according to StorageProfiles | ||
| func RenderPvc(ctx context.Context, client client.Client, pvc *v1.PersistentVolumeClaim) error { |
There was a problem hiding this comment.
What about setting virt default storage class if storageclass is unset?
There was a problem hiding this comment.
We get it for free from the call to renderPvcSpecVolumeModeAndAccessModes() which uses GetStorageClassByNameWithVirtFallback().
There was a problem hiding this comment.
Okay do you think it would make sense to extract that bit and create a renderPvcSpecStorageClass function? Or change renderPvcSpecVolumeModeAndAccessModes to renderPvcSpecVolumeModeAndAccessModesAndStorageClass?
| return nil | ||
| } | ||
|
|
||
| crd := args.CurrentObject.(*extv1.CustomResourceDefinition) |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
| return err | ||
| } | ||
| if enabled { | ||
| whc = cluster.CreatePvcMutatingWebhook(args.Namespace, args.Client, args.Logger) |
There was a problem hiding this comment.
I think to be consistent with the other resources created by callbacks, maybe put CreatePvcMutatingWebhook in this package.
There was a problem hiding this comment.
And don't forget to make sure it is owned by CDI
There was a problem hiding this comment.
Move it to this package although all other similar webhook CR creation code is in the cluster package? much harder to maintain
There was a problem hiding this comment.
And don't forget to make sure it is owned by CDI
fixed
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Moved. I buy it when you explain it this way.
awels
left a comment
There was a problem hiding this comment.
Looks pretty good to me, a few nits.
|
|
||
| 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. |
There was a problem hiding this comment.
| 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. |
| } | ||
| volumeMode := v1.PersistentVolumeFilesystem | ||
| pvcSpec.VolumeMode = &volumeMode | ||
| pvcSpec.VolumeMode = &cc.FilesystemMode |
There was a problem hiding this comment.
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]>
| } | ||
|
|
||
| deployment, ok := args.DesiredObject.(*appsv1.Deployment) | ||
| if !ok || deployment.Name != "cdi-apiserver" { |
There was a problem hiding this comment.
check out recently added CDIApiServerResourceName const
|
/retest |
|
/lgtm |
| }, | ||
| } | ||
|
|
||
| if c == nil { |
There was a problem hiding this comment.
This should never be the case in this context
Signed-off-by: Arnon Gilboa <[email protected]>
| err = args.Client.Get(context.TODO(), key, whc) | ||
| if err == nil { | ||
| if enabled { | ||
| return nil |
There was a problem hiding this comment.
Can't exit here. May have to update the webhook configuration when:
- user changes any "critical" value
- but perhaps most importantly, whenever the cacert gets rotated the cabundle will change
Signed-off-by: Arnon Gilboa <[email protected]>
| return err | ||
| } | ||
| if !reflect.DeepEqual(whc, whcCopy) { | ||
| return args.Client.Update(context.TODO(), whc) |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Great catch & great lesson learned. Fixed.
Signed-off-by: Arnon Gilboa <[email protected]>
|
/approve |
|
[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 DetailsNeeds approval from an approver in each of these files:
Approvers can indicate their approval by writing |
|
/lgtm |
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
WebhookPvcRenderingfeature gate.For any PVC you want to use StorageProfile, label it with
cdi.kubevirt.io/useStorageProfile: "true"If you want to use
volumeModepreferred by CDI according toStorageProfiles, set it toFromStorageProfile. Otherwise if not explicitly set toBlock, it will beFilesystemby k8s default.E.g.:
Special notes for your reviewer:
Release note: