local-path-storage: Update provisioner to v0.0.37.

Rebase the manifest on upstream v0.0.37: health probes, a namespaced
Role for helper pods, and narrower cluster RBAC.  The provisioner now
passes the volume path as $VOL_DIR instead of -p, so setup.sh and
teardown.sh accept both.  Allow the hostPID helper pod template, which
v0.0.37 otherwise rejects; the ZFS hook needs it for nsenter.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change-Id: Ib8a64ac42caa0a8369c49393aa242f40116b1680
diff --git a/local-path-storage/local-path-storage.yaml b/local-path-storage/local-path-storage.yaml
index 6046baa..cc80544 100644
--- a/local-path-storage/local-path-storage.yaml
+++ b/local-path-storage/local-path-storage.yaml
@@ -1,67 +1,87 @@
----
+# local-path-provisioner v0.0.37, based on upstream deploy/local-path-storage.yaml
+# with these local changes:
+#   - PVs live under /var/k8s; setup.sh/teardown.sh create and unmount one
+#     ZFS dataset per PV (rpool/var/k8s/<pv>) in the host mount namespace.
+#   - The helper pod needs hostPID and privileged for nsenter.
+#   - The namespace (PSA enforce=privileged) is not managed here.
+# setup and teardown below must stay in sync with setup.sh and teardown.sh.
+# The provisioner reads helperPod.yaml only at startup; restart it after
+# changing that.
 apiVersion: apps/v1
 kind: Deployment
 metadata:
-  annotations:
-    kubectl.kubernetes.io/last-applied-configuration: |
-      {"apiVersion":"apps/v1beta2","kind":"Deployment","metadata":{"annotations":{},"name":"local-path-provisioner","namespace":"local-path-storage"},"spec":{"replicas":1,"selector":{"matchLabels":{"app":"local-path-provisioner"}},"template":{"metadata":{"labels":{"app":"local-path-provisioner"}},"spec":{"containers":[{"command":["local-path-provisioner","--debug","start","--config","/etc/config/config.json"],"env":[{"name":"POD_NAMESPACE","valueFrom":{"fieldRef":{"fieldPath":"metadata.namespace"}}}],"image":"rancher/local-path-provisioner:v0.0.2","imagePullPolicy":"Always","name":"local-path-provisioner","volumeMounts":[{"mountPath":"/etc/config/","name":"config-volume"}]}],"serviceAccountName":"local-path-provisioner-service-account","volumes":[{"configMap":{"name":"local-path-config"},"name":"config-volume"}]}}}}
-  labels:
-    app: local-path-provisioner
   name: local-path-provisioner
   namespace: local-path-storage
+  labels:
+    app: local-path-provisioner
 spec:
-  progressDeadlineSeconds: 600
   replicas: 1
-  revisionHistoryLimit: 10
   selector:
     matchLabels:
       app: local-path-provisioner
-  strategy:
-    rollingUpdate:
-      maxSurge: 25%
-      maxUnavailable: 25%
-    type: RollingUpdate
   template:
     metadata:
       labels:
         app: local-path-provisioner
     spec:
+      serviceAccountName: local-path-provisioner-service-account
       containers:
-        - command:
+        - name: local-path-provisioner
+          image: docker.io/rancher/local-path-provisioner:v0.0.37
+          imagePullPolicy: IfNotPresent
+          command:
             - local-path-provisioner
             - --debug
             - start
             - --config
             - /etc/config/config.json
+            # The helper pod needs hostPID for nsenter (see setup.sh).
+            - --allow-unsafe-helper-pod-template
+          ports:
+            - name: health
+              containerPort: 8080
+              protocol: TCP
+          startupProbe:
+            httpGet:
+              path: /health
+              port: health
+            initialDelaySeconds: 5
+            periodSeconds: 5
+            failureThreshold: 12
+          livenessProbe:
+            httpGet:
+              path: /health
+              port: health
+            initialDelaySeconds: 10
+            periodSeconds: 10
+          readinessProbe:
+            httpGet:
+              path: /ready
+              port: health
+            initialDelaySeconds: 5
+            periodSeconds: 5
+          volumeMounts:
+            - name: config-volume
+              mountPath: /etc/config/
           env:
             - name: POD_NAMESPACE
               valueFrom:
                 fieldRef:
-                  apiVersion: v1
                   fieldPath: metadata.namespace
-          image: rancher/local-path-provisioner:v0.0.19
-          imagePullPolicy: Always
-          name: local-path-provisioner
-          resources: {}
-          terminationMessagePath: /dev/termination-log
-          terminationMessagePolicy: File
-          volumeMounts:
-            - mountPath: /etc/config/
-              name: config-volume
-      dnsPolicy: ClusterFirst
-      restartPolicy: Always
-      schedulerName: default-scheduler
-      securityContext: {}
-      serviceAccount: local-path-provisioner-service-account
-      serviceAccountName: local-path-provisioner-service-account
-      terminationGracePeriodSeconds: 30
+            - name: CONFIG_MOUNT_PATH
+              value: /etc/config/
+            - name: HEALTH_PORT
+              value: "8080"
       volumes:
-        - configMap:
-            defaultMode: 420
+        - name: config-volume
+          configMap:
             name: local-path-config
-          name: config-volume
 ---
 apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: local-path-config
+  namespace: local-path-storage
 data:
   config.json: |-
     {
@@ -72,14 +92,20 @@
             }
             ]
     }
-  helperPod.yaml: "apiVersion: v1\nkind: Pod\nmetadata:\n  name: helper-pod\nspec:\n  hostPID: true\n  containers:\n  - name: helper-pod\n    image: alpine:3.20\n    securityContext:\n      privileged: true\n"
-  setup: "#!/bin/sh\n# Creates a ZFS dataset per PV.  The helper pod runs with hostPID, and all\n# ZFS and filesystem operations are performed in the host's mount namespace\n# (nsenter -t 1 -m).  Running zfs directly inside the helper container would\n# mount the dataset only inside the container's own mount namespace; the\n# workload would then write into the parent dataset, and the child dataset\n# would shadow that data after the next host reboot.\nset -eu\n\nwhile getopts \"m:s:p:\" opt\ndo\n    case $opt in\n        p)\n        absolutePath=$OPTARG\n        ;;\n        s)\n        sizeInBytes=$OPTARG\n        ;;\n        m)\n        volMode=$OPTARG\n        ;;\n    esac\ndone\n\nhost() { nsenter -t 1 -m -- \"$@\"; }\n\nhost zfs create \"rpool${absolutePath}\"\nhost zfs mount \"rpool${absolutePath}\" 2>/dev/null || :\nhost findmnt -n -o SOURCE \"${absolutePath}\" | grep -qx \"rpool${absolutePath}\"\nhost chmod 0777 \"${absolutePath}\"\n"
-  teardown: |
+  setup: |
     #!/bin/sh
-    # Unmounts (but keeps) the PV's ZFS dataset.  Runs in the host's mount
-    # namespace; see setup.sh.
+    # Creates a ZFS dataset per PV.  The helper pod runs with hostPID, and all
+    # ZFS and filesystem operations are performed in the host's mount namespace
+    # (nsenter -t 1 -m).  Running zfs directly inside the helper container would
+    # mount the dataset only inside the container's own mount namespace; the
+    # workload would then write into the parent dataset, and the child dataset
+    # would shadow that data after the next host reboot.
+    #
+    # The provisioner passes the volume path as $VOL_DIR (v0.0.2x and later);
+    # older versions passed it as -p.
     set -eu
 
+    absolutePath=${VOL_DIR:-}
     while getopts "m:s:p:" opt
     do
         case $opt in
@@ -94,25 +120,89 @@
             ;;
         esac
     done
+    [ -n "$absolutePath" ] || { echo "setup: no volume path given" >&2; exit 1; }
+
+    host() { nsenter -t 1 -m -- "$@"; }
+
+    host zfs create "rpool${absolutePath}"
+    host zfs mount "rpool${absolutePath}" 2>/dev/null || :
+    host findmnt -n -o SOURCE "${absolutePath}" | grep -qx "rpool${absolutePath}"
+    host chmod 0777 "${absolutePath}"
+  teardown: |
+    #!/bin/sh
+    # Unmounts (but keeps) the PV's ZFS dataset.  Runs in the host's mount
+    # namespace; see setup.sh.
+    set -eu
+
+    absolutePath=${VOL_DIR:-}
+    while getopts "m:s:p:" opt
+    do
+        case $opt in
+            p)
+            absolutePath=$OPTARG
+            ;;
+            s)
+            sizeInBytes=$OPTARG
+            ;;
+            m)
+            volMode=$OPTARG
+            ;;
+        esac
+    done
+    [ -n "$absolutePath" ] || { echo "teardown: no volume path given" >&2; exit 1; }
 
     nsenter -t 1 -m -- zfs set mountpoint=none "rpool${absolutePath}"
-kind: ConfigMap
-metadata:
-  annotations:
-    kubectl.kubernetes.io/last-applied-configuration: |
-      {"apiVersion":"v1","data":{"config.json":"{\n        \"nodePathMap\":[\n        {\n                \"node\":\"DEFAULT_PATH_FOR_NON_LISTED_NODES\",\n                \"paths\":[\"/var/k8s\"]\n        }\n        ]\n}"},"kind":"ConfigMap","metadata":{"annotations":{},"name":"local-path-config","namespace":"local-path-storage"}}
-  name: local-path-config
-  namespace: local-path-storage
+  helperPod.yaml: |
+    apiVersion: v1
+    kind: Pod
+    metadata:
+      name: helper-pod
+    spec:
+      priorityClassName: system-node-critical
+      tolerations:
+        - key: node.kubernetes.io/disk-pressure
+          operator: Exists
+          effect: NoSchedule
+      hostPID: true
+      containers:
+      - name: helper-pod
+        image: alpine:3.20
+        imagePullPolicy: IfNotPresent
+        securityContext:
+          privileged: true
 ---
 apiVersion: v1
 kind: ServiceAccount
 metadata:
-  annotations:
-    kubectl.kubernetes.io/last-applied-configuration: |
-      {"apiVersion":"v1","kind":"ServiceAccount","metadata":{"annotations":{},"name":"local-path-provisioner-service-account","namespace":"local-path-storage"}}
   name: local-path-provisioner-service-account
   namespace: local-path-storage
 ---
+# Since v0.0.24 helper pods run in the provisioner's namespace.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: local-path-provisioner-role
+  namespace: local-path-storage
+rules:
+  - apiGroups: [""]
+    resources: ["pods"]
+    verbs: ["get", "list", "watch", "create", "patch", "update", "delete"]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: local-path-provisioner-bind
+  namespace: local-path-storage
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: Role
+  name: local-path-provisioner-role
+subjects:
+  - kind: ServiceAccount
+    name: local-path-provisioner-service-account
+    namespace: local-path-storage
+---
+# Leftover from PodSecurityPolicy times (psp:default no longer grants anything).
 apiVersion: rbac.authorization.k8s.io/v1
 kind: RoleBinding
 metadata:
@@ -130,51 +220,24 @@
 apiVersion: rbac.authorization.k8s.io/v1
 kind: ClusterRole
 metadata:
-  annotations:
-    kubectl.kubernetes.io/last-applied-configuration: |
-      {"apiVersion":"rbac.authorization.k8s.io/v1beta1","kind":"ClusterRole","metadata":{"annotations":{},"name":"local-path-provisioner-role"},"rules":[{"apiGroups":[""],"resources":["nodes","persistentvolumeclaims"],"verbs":["get","list","watch"]},{"apiGroups":[""],"resources":["endpoints","persistentvolumes","pods"],"verbs":["*"]},{"apiGroups":[""],"resources":["events"],"verbs":["create","patch"]},{"apiGroups":["storage.k8s.io"],"resources":["storageclasses"],"verbs":["get","list","watch"]}]}
   name: local-path-provisioner-role
 rules:
-  - apiGroups:
-      - ""
-    resources:
-      - nodes
-      - persistentvolumeclaims
-      - configmaps
-    verbs:
-      - get
-      - list
-      - watch
-  - apiGroups:
-      - ""
-    resources:
-      - endpoints
-      - persistentvolumes
-      - pods
-    verbs:
-      - '*'
-  - apiGroups:
-      - ""
-    resources:
-      - events
-    verbs:
-      - create
-      - patch
-  - apiGroups:
-      - storage.k8s.io
-    resources:
-      - storageclasses
-    verbs:
-      - get
-      - list
-      - watch
+  - apiGroups: [""]
+    resources: ["nodes", "persistentvolumeclaims", "configmaps", "pods", "pods/log"]
+    verbs: ["get", "list", "watch"]
+  - apiGroups: [""]
+    resources: ["persistentvolumes"]
+    verbs: ["get", "list", "watch", "create", "patch", "update", "delete"]
+  - apiGroups: [""]
+    resources: ["events"]
+    verbs: ["create", "patch"]
+  - apiGroups: ["storage.k8s.io"]
+    resources: ["storageclasses"]
+    verbs: ["get", "list", "watch"]
 ---
 apiVersion: rbac.authorization.k8s.io/v1
 kind: ClusterRoleBinding
 metadata:
-  annotations:
-    kubectl.kubernetes.io/last-applied-configuration: |
-      {"apiVersion":"rbac.authorization.k8s.io/v1beta1","kind":"ClusterRoleBinding","metadata":{"annotations":{},"name":"local-path-provisioner-bind"},"roleRef":{"apiGroup":"rbac.authorization.k8s.io","kind":"ClusterRole","name":"local-path-provisioner-role"},"subjects":[{"kind":"ServiceAccount","name":"local-path-provisioner-service-account","namespace":"local-path-storage"}]}
   name: local-path-provisioner-bind
 roleRef:
   apiGroup: rbac.authorization.k8s.io
@@ -188,10 +251,7 @@
 apiVersion: storage.k8s.io/v1
 kind: StorageClass
 metadata:
-  annotations:
-    kubectl.kubernetes.io/last-applied-configuration: |
-      {"apiVersion":"storage.k8s.io/v1","kind":"StorageClass","metadata":{"annotations":{},"name":"local-path"},"provisioner":"rancher.io/local-path","reclaimPolicy":"Delete","volumeBindingMode":"WaitForFirstConsumer"}
   name: local-path
 provisioner: rancher.io/local-path
-reclaimPolicy: Delete
 volumeBindingMode: WaitForFirstConsumer
+reclaimPolicy: Delete
diff --git a/local-path-storage/setup.sh b/local-path-storage/setup.sh
index aff8c0c..4653e9f 100644
--- a/local-path-storage/setup.sh
+++ b/local-path-storage/setup.sh
@@ -5,8 +5,12 @@
 # mount the dataset only inside the container's own mount namespace; the
 # workload would then write into the parent dataset, and the child dataset
 # would shadow that data after the next host reboot.
+#
+# The provisioner passes the volume path as $VOL_DIR (v0.0.2x and later);
+# older versions passed it as -p.
 set -eu
 
+absolutePath=${VOL_DIR:-}
 while getopts "m:s:p:" opt
 do
     case $opt in
@@ -21,6 +25,7 @@
         ;;
     esac
 done
+[ -n "$absolutePath" ] || { echo "setup: no volume path given" >&2; exit 1; }
 
 host() { nsenter -t 1 -m -- "$@"; }
 
diff --git a/local-path-storage/teardown.sh b/local-path-storage/teardown.sh
index 75c6e9e..6a75f50 100644
--- a/local-path-storage/teardown.sh
+++ b/local-path-storage/teardown.sh
@@ -3,6 +3,7 @@
 # namespace; see setup.sh.
 set -eu
 
+absolutePath=${VOL_DIR:-}
 while getopts "m:s:p:" opt
 do
     case $opt in
@@ -17,5 +18,6 @@
         ;;
     esac
 done
+[ -n "$absolutePath" ] || { echo "teardown: no volume path given" >&2; exit 1; }
 
 nsenter -t 1 -m -- zfs set mountpoint=none "rpool${absolutePath}"