From 14dd63c1efe071a5a10e4487b817f26d17542be3 Mon Sep 17 00:00:00 2001 From: Vicente Cheng Date: Thu, 24 Oct 2024 09:33:34 +0800 Subject: [PATCH] webhook: add StorageClass validator - To ensure the VG status is active Signed-off-by: Vicente Cheng --- cmd/node-disk-manager-webhook/main.go | 5 ++ .../templates/rbac.yaml | 4 +- pkg/webhook/storageclass/validator.go | 68 +++++++++++++++++++ 3 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 pkg/webhook/storageclass/validator.go diff --git a/cmd/node-disk-manager-webhook/main.go b/cmd/node-disk-manager-webhook/main.go index 76d7c7e2..30e76470 100644 --- a/cmd/node-disk-manager-webhook/main.go +++ b/cmd/node-disk-manager-webhook/main.go @@ -22,12 +22,14 @@ import ( ctldisk "github.com/harvester/node-disk-manager/pkg/generated/controllers/harvesterhci.io" ctldiskv1 "github.com/harvester/node-disk-manager/pkg/generated/controllers/harvesterhci.io/v1beta1" "github.com/harvester/node-disk-manager/pkg/webhook/blockdevice" + "github.com/harvester/node-disk-manager/pkg/webhook/storageclass" ) const webhookName = "harvester-node-disk-manager-webhook" type resourceCaches struct { bdCache ctldiskv1.BlockDeviceCache + lvmVGCache ctldiskv1.LVMVolumeGroupCache storageClassCache ctlstoragev1.StorageClassCache pvCache ctlcorev1.PersistentVolumeCache } @@ -116,8 +118,10 @@ func runWebhookServer(ctx context.Context, cfg *rest.Config, options *config.Opt } bdValidator := blockdevice.NewBlockdeviceValidator(resourceCaches.bdCache, resourceCaches.storageClassCache, resourceCaches.pvCache) + scValidator := storageclass.NewStorageClassValidator(resourceCaches.lvmVGCache) var validators = []admission.Validator{ bdValidator, + scValidator, } if err := webhookServer.RegisterMutators(mutators...); err != nil { @@ -155,6 +159,7 @@ func newCaches(ctx context.Context, cfg *rest.Config, threadiness int) (*resourc starters = append(starters, disks, storageFactory, coreFactory) resourceCaches := &resourceCaches{ bdCache: disks.Harvesterhci().V1beta1().BlockDevice().Cache(), + lvmVGCache: disks.Harvesterhci().V1beta1().LVMVolumeGroup().Cache(), storageClassCache: storageFactory.Storage().V1().StorageClass().Cache(), pvCache: coreFactory.Core().V1().PersistentVolume().Cache(), } diff --git a/deploy/charts/harvester-node-disk-manager/templates/rbac.yaml b/deploy/charts/harvester-node-disk-manager/templates/rbac.yaml index dbf38b6a..593ee146 100644 --- a/deploy/charts/harvester-node-disk-manager/templates/rbac.yaml +++ b/deploy/charts/harvester-node-disk-manager/templates/rbac.yaml @@ -55,9 +55,9 @@ rules: verbs: [ "get", "watch", "list" ] - apiGroups: [ "storage.k8s.io" ] resources: [ "storageclasses" ] - verbs: [ "get", "watch", "list" ] + verbs: [ "*" ] - apiGroups: [ "harvesterhci.io" ] - resources: [ "blockdevices" ] + resources: [ "blockdevices", "lvmvolumegroups", "lvmvolumegroups/status" ] verbs: [ "*" ] - apiGroups: [ "apiregistration.k8s.io" ] resources: [ "apiservices" ] diff --git a/pkg/webhook/storageclass/validator.go b/pkg/webhook/storageclass/validator.go new file mode 100644 index 00000000..e50a0724 --- /dev/null +++ b/pkg/webhook/storageclass/validator.go @@ -0,0 +1,68 @@ +package storageclass + +import ( + "fmt" + + werror "github.com/harvester/webhook/pkg/error" + "github.com/harvester/webhook/pkg/server/admission" + admissionregv1 "k8s.io/api/admissionregistration/v1" + storagev1 "k8s.io/api/storage/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime" + + diskv1 "github.com/harvester/node-disk-manager/pkg/apis/harvesterhci.io/v1beta1" + ctldiskv1 "github.com/harvester/node-disk-manager/pkg/generated/controllers/harvesterhci.io/v1beta1" + "github.com/harvester/node-disk-manager/pkg/utils" +) + +type Validator struct { + admission.DefaultValidator + + lvmVGCache ctldiskv1.LVMVolumeGroupCache +} + +func NewStorageClassValidator(lvmVGCache ctldiskv1.LVMVolumeGroupCache) *Validator { + return &Validator{ + lvmVGCache: lvmVGCache, + } +} + +func (v *Validator) Create(_ *admission.Request, newObj runtime.Object) error { + sc := newObj.(*storagev1.StorageClass) + return v.validateVGStatus(sc) +} + +func (v *Validator) validateVGStatus(sc *storagev1.StorageClass) error { + if sc.Provisioner != utils.LVMCSIDriver { + return nil + } + vgs, err := v.lvmVGCache.List(utils.HarvesterNS, labels.Everything()) + if err != nil { + return err + } + targetVGName := sc.Parameters["vgName"] + + for _, vg := range vgs { + if vg.Spec.VgName != targetVGName { + continue + } + if vg.Status == nil || vg.Status.Status != diskv1.VGStatusActive { + errMsg := fmt.Sprintf("VG %s is not ready", vg.Spec.VgName) + return werror.NewBadRequest(errMsg) + } + } + return nil +} + +func (v *Validator) Resource() admission.Resource { + return admission.Resource{ + Names: []string{"storageclasses"}, + Scope: admissionregv1.ClusterScope, + APIGroup: storagev1.SchemeGroupVersion.Group, + APIVersion: storagev1.SchemeGroupVersion.Version, + ObjectType: &storagev1.StorageClass{}, + OperationTypes: []admissionregv1.OperationType{ + admissionregv1.Create, + }, + } +}