From af85381fd304f3a65999687a5da18536e8a1b1ea Mon Sep 17 00:00:00 2001 From: Alban Bedel Date: Tue, 23 Jan 2024 17:47:44 +0100 Subject: [PATCH] Add support for custom path patterns Add support for the `pathPattern` parameter, if set it is expanded as a go template with the PV name and PVC metadata as input. This allow configuring the provisioner to use predictable paths so volumes can be prefilled externally or re-used. Signed-off-by: Alban Bedel --- README.md | 6 ++++-- provisioner.go | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7ccae2ad5..a4ade6456 100644 --- a/README.md +++ b/README.md @@ -275,8 +275,9 @@ A few things to note; the annotation for the `StorageClass` will apply to all vo ### Storage classes -If more than one `paths` are specified in the `nodePathMap` the path is chosen randomly. To make the provisioner choose a specific path, use a `storageClass` defined with a parameter called `nodePath`. Note that this path should be defined in the `nodePathMap` +If more than one `paths` are specified in the `nodePathMap` the path is chosen randomly. To make the provisioner choose a specific path, use a `storageClass` defined with a parameter called `nodePath`. Note that this path should be defined in the `nodePathMap`. +By default the volume subdirectory is named using the template `{{ .PVName }}_{{ .PVC.Namespace }}_{{ .PVC.Name }}` which make the directory specific to the PV instance. The template can be changed using the `pathPattern` parameter which is interpreted as a go template. The template has access to the PV name using the `PVName` variable and the PVC metadata object, including labels and annotations, with the `PVC` variable. ``` apiVersion: storage.k8s.io/v1 kind: StorageClass @@ -285,11 +286,12 @@ metadata: provisioner: rancher.io/local-path parameters: nodePath: /data/ssd + pathPattern: "{{ .PVC.Namespace }}/{{ .PVC.Name }}" volumeBindingMode: WaitForFirstConsumer reclaimPolicy: Delete ``` -Here the provisioner will use the path `/data/ssd` when storage class `ssd-local-path` is used. +Here the provisioner will use the path `/data/ssd` with a subdirectory per namespace and PVC when storage class `ssd-local-path` is used. ## Uninstall diff --git a/provisioner.go b/provisioner.go index e12485325..325797285 100644 --- a/provisioner.go +++ b/provisioner.go @@ -12,6 +12,7 @@ import ( "strconv" "strings" "sync" + "text/template" "time" "github.com/Sirupsen/logrus" @@ -291,6 +292,31 @@ func (p *LocalPathProvisioner) pickConfig(storageClassName string) (*StorageClas return &cfg, nil } +type pvMetadata struct { + PVName string + PVC metav1.ObjectMeta +} + +func pathFromPattern(pattern string, opts pvController.ProvisionOptions) (string, error) { + metadata := pvMetadata{ + PVName: opts.PVName, + PVC: opts.PVC.ObjectMeta, + } + + tpl, err := template.New("pathPattern").Parse(pattern) + if err != nil { + return "", err + } + + buf := new(bytes.Buffer) + err = tpl.Execute(buf, metadata) + if err != nil { + return "", err + } + + return buf.String(), nil +} + func (p *LocalPathProvisioner) Provision(ctx context.Context, opts pvController.ProvisionOptions) (*v1.PersistentVolume, pvController.ProvisioningState, error) { cfg, err := p.pickConfig(opts.StorageClass.Name) if err != nil { @@ -340,6 +366,15 @@ func (p *LocalPathProvisioner) provisionFor(opts pvController.ProvisionOptions, name := opts.PVName folderName := strings.Join([]string{name, opts.PVC.Namespace, opts.PVC.Name}, "_") + pathPattern, exists := opts.StorageClass.Parameters["pathPattern"] + if exists { + folderName, err = pathFromPattern(pathPattern, opts) + if err != nil { + err = errors.Wrapf(err, "failed to create path from pattern %v", pathPattern) + return nil, pvController.ProvisioningFinished, err + } + } + path := filepath.Join(basePath, folderName) if nodeName == "" { logrus.Infof("Creating volume %v at %v", name, path)