{{- /* csi-snapshotter — vendored from kubernetes-csi/external-snapshotter v8.1.0. Required by Longhorn's CSI VolumeSnapshot path (Phase 3 of ADR 0003). Longhorn ships csi-snapshotter SIDECARS but does not ship the snapshot-controller + standard snapshot.storage.k8s.io CRDs — those are decoupled from any specific CSI driver. Tower's spawn-env / Refresh ↓ uses the standard CSI VolumeSnapshot API (`snapshot.storage.k8s.io/v1`) so the orchestration code is portable across CSI drivers. Without these CRDs + controller, the VolumeSnapshot kind doesn't exist and every snapshot-based op fails. Source manifests: https://github.com/kubernetes-csi/external-snapshotter/tree/v8.1.0 Pin: v8.1.0 (current upstream stable as of 2026-05). Why one big template instead of `crds/`: Argo doesn't process Helm's `crds/` directory — only `templates/`. So CRDs land here, with `helm.sh/hook: crd-install` annotations to make the ordering safe on first install (CRDs apply before any CR that references them). Toggle: `.Values.csiSnapshotter.enabled` (default true). Disable only on clusters where the snapshotter is provisioned out-of-band. */ -}} {{- if .Values.csiSnapshotter.enabled }} --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: name: volumesnapshotclasses.snapshot.storage.k8s.io annotations: argocd.argoproj.io/sync-options: ServerSideApply=true api-approved.kubernetes.io: "https://github.com/kubernetes-csi/external-snapshotter/pull/814" spec: group: snapshot.storage.k8s.io names: kind: VolumeSnapshotClass listKind: VolumeSnapshotClassList plural: volumesnapshotclasses shortNames: [vsclass, vsclasses] singular: volumesnapshotclass scope: Cluster versions: - additionalPrinterColumns: - jsonPath: .driver name: Driver type: string - description: Determines whether a VolumeSnapshotContent created through the VolumeSnapshotClass should be deleted when its bound VolumeSnapshot is deleted. jsonPath: .deletionPolicy name: DeletionPolicy type: string - jsonPath: .metadata.creationTimestamp name: Age type: date name: v1 schema: openAPIV3Schema: description: VolumeSnapshotClass specifies parameters that a underlying storage system uses when creating a volume snapshot. properties: apiVersion: type: string deletionPolicy: description: deletionPolicy determines whether a VolumeSnapshotContent created through the VolumeSnapshotClass should be deleted when its bound VolumeSnapshot is deleted. Required. enum: [Delete, Retain] type: string driver: description: driver is the name of the storage driver that handles this VolumeSnapshotClass. Required. type: string kind: type: string metadata: type: object parameters: additionalProperties: type: string description: parameters is a key-value map with storage driver specific parameters for creating snapshots. type: object required: [deletionPolicy, driver] type: object served: true storage: true subresources: {} --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: name: volumesnapshotcontents.snapshot.storage.k8s.io annotations: argocd.argoproj.io/sync-options: ServerSideApply=true api-approved.kubernetes.io: "https://github.com/kubernetes-csi/external-snapshotter/pull/814" spec: group: snapshot.storage.k8s.io names: kind: VolumeSnapshotContent listKind: VolumeSnapshotContentList plural: volumesnapshotcontents shortNames: [vsc, vscs] singular: volumesnapshotcontent scope: Cluster versions: - additionalPrinterColumns: - jsonPath: .status.readyToUse name: ReadyToUse type: boolean - jsonPath: .status.restoreSize name: RestoreSize type: integer - jsonPath: .spec.deletionPolicy name: DeletionPolicy type: string - jsonPath: .spec.driver name: Driver type: string - jsonPath: .spec.volumeSnapshotClassName name: VolumeSnapshotClass type: string - jsonPath: .spec.volumeSnapshotRef.name name: VolumeSnapshot type: string - jsonPath: .spec.volumeSnapshotRef.namespace name: VolumeSnapshotNamespace type: string - jsonPath: .metadata.creationTimestamp name: Age type: date name: v1 schema: openAPIV3Schema: description: VolumeSnapshotContent represents the actual "on-disk" snapshot object in the underlying storage system type: object x-kubernetes-preserve-unknown-fields: true served: true storage: true subresources: status: {} --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: name: volumesnapshots.snapshot.storage.k8s.io annotations: argocd.argoproj.io/sync-options: ServerSideApply=true api-approved.kubernetes.io: "https://github.com/kubernetes-csi/external-snapshotter/pull/814" spec: group: snapshot.storage.k8s.io names: kind: VolumeSnapshot listKind: VolumeSnapshotList plural: volumesnapshots shortNames: [vs] singular: volumesnapshot scope: Namespaced versions: - additionalPrinterColumns: - description: Indicates if the snapshot is ready to be used to restore a volume. jsonPath: .status.readyToUse name: ReadyToUse type: boolean - description: If a new snapshot needs to be created, this contains the name of the source PVC from which this snapshot was (or will be) created. jsonPath: .spec.source.persistentVolumeClaimName name: SourcePVC type: string - description: If a snapshot already exists, this contains the name of the existing VolumeSnapshotContent object. jsonPath: .spec.source.volumeSnapshotContentName name: SourceSnapshotContent type: string - description: Represents the minimum size of volume required to rehydrate from this snapshot. jsonPath: .status.restoreSize name: RestoreSize type: string - description: The name of the VolumeSnapshotClass requested by the VolumeSnapshot. jsonPath: .spec.volumeSnapshotClassName name: SnapshotClass type: string - description: Name of the VolumeSnapshotContent object to which the VolumeSnapshot object intends to bind to. jsonPath: .status.boundVolumeSnapshotContentName name: SnapshotContent type: string - description: Timestamp when the point-in-time snapshot was taken by the underlying storage system. jsonPath: .status.creationTime name: CreationTime type: date - jsonPath: .metadata.creationTimestamp name: Age type: date name: v1 schema: openAPIV3Schema: description: VolumeSnapshot is a user's request for either creating a point-in-time snapshot of a persistent volume, or binding to a pre-existing snapshot. type: object x-kubernetes-preserve-unknown-fields: true served: true storage: true subresources: status: {} --- # RBAC + controller deployment for the snapshot-controller. Lives # in kube-system per upstream convention. The controller watches # VolumeSnapshot CRs and orchestrates the CSI driver-side snapshot # (in our case Longhorn's csi-snapshotter sidecar does the actual # work; this controller bridges the standard CRD <-> CSI calls). apiVersion: v1 kind: ServiceAccount metadata: name: snapshot-controller namespace: kube-system --- kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: name: snapshot-controller-runner rules: - apiGroups: [""] resources: [persistentvolumes] verbs: [get, list, watch] - apiGroups: [""] resources: [persistentvolumeclaims] verbs: [get, list, watch, update] - apiGroups: [storage.k8s.io] resources: [storageclasses] verbs: [get, list, watch] - apiGroups: [""] resources: [events] verbs: [list, watch, create, update, patch] - apiGroups: [snapshot.storage.k8s.io] resources: [volumesnapshotclasses] verbs: [get, list, watch] - apiGroups: [snapshot.storage.k8s.io] resources: [volumesnapshotcontents] verbs: [create, get, list, watch, update, delete, patch] - apiGroups: [snapshot.storage.k8s.io] resources: [volumesnapshotcontents/status] verbs: [patch] - apiGroups: [snapshot.storage.k8s.io] resources: [volumesnapshots] verbs: [get, list, watch, update, patch, delete] - apiGroups: [snapshot.storage.k8s.io] resources: [volumesnapshots/status] verbs: [update, patch] --- kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: snapshot-controller-role subjects: - kind: ServiceAccount name: snapshot-controller namespace: kube-system roleRef: kind: ClusterRole name: snapshot-controller-runner apiGroup: rbac.authorization.k8s.io --- kind: Role apiVersion: rbac.authorization.k8s.io/v1 metadata: namespace: kube-system name: snapshot-controller-leaderelection rules: - apiGroups: [coordination.k8s.io] resources: [leases] verbs: [get, watch, list, delete, update, create] --- kind: RoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: snapshot-controller-leaderelection namespace: kube-system subjects: - kind: ServiceAccount name: snapshot-controller namespace: kube-system roleRef: kind: Role name: snapshot-controller-leaderelection apiGroup: rbac.authorization.k8s.io --- kind: Deployment apiVersion: apps/v1 metadata: name: snapshot-controller namespace: kube-system spec: replicas: 2 selector: matchLabels: app.kubernetes.io/name: snapshot-controller template: metadata: labels: app.kubernetes.io/name: snapshot-controller spec: serviceAccountName: snapshot-controller containers: - name: snapshot-controller image: registry.k8s.io/sig-storage/snapshot-controller:v8.1.0 args: - --v=5 - --leader-election=true - --retry-crd-interval-max=30s imagePullPolicy: IfNotPresent {{- end }} {{- if and .Values.longhorn.enabled .Values.csiSnapshotter.enabled }} --- # VolumeSnapshotClass for Tower's CSI VolumeClone path. type=snap # is Longhorn's in-place CoW snapshot (fast, local). type=bak # is the slower S3-bound block backup, used by Phase 5 only. apiVersion: snapshot.storage.k8s.io/v1 kind: VolumeSnapshotClass metadata: name: longhorn-snapshot-class driver: driver.longhorn.io deletionPolicy: Delete parameters: type: snap {{- end }}