Files

128 lines
4.7 KiB
YAML

# Storage certification job.
#
# A PersistentVolumeClaim is not a filesystem contract. Whether an atomic rename, a same-file-store
# guarantee, or symlink refusal actually holds depends on the CSI driver, the StorageClass, the
# access mode, the backend, and the mount options — so this job records all five alongside the probe
# result. A certification without that tuple is not transferable to another cluster.
#
# The job writes a machine-readable result to the claim itself so the evidence lives with the volume
# it describes.
#
# kubectl apply -f infra/fileserver/kubernetes/pvc-certification-job.yaml
# kubectl logs job/fileserver-pvc-certification
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: fileserver-certification
labels:
app.kubernetes.io/name: fileserver
app.kubernetes.io/component: certification
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
# Left unset on purpose: the certification is only meaningful for the class it actually ran on,
# so the operator names it explicitly rather than inheriting a cluster default.
storageClassName: ""
---
apiVersion: batch/v1
kind: Job
metadata:
name: fileserver-pvc-certification
labels:
app.kubernetes.io/name: fileserver
app.kubernetes.io/component: certification
spec:
backoffLimit: 0
template:
metadata:
labels:
app.kubernetes.io/name: fileserver
app.kubernetes.io/component: certification
spec:
restartPolicy: Never
securityContext:
runAsNonRoot: true
runAsUser: 10001
fsGroup: 10001
containers:
- name: certify
image: eclipse-temurin:21-jdk
env:
- name: FILESERVER_STORAGE_ROOT
value: /var/lib/backend/files
- name: KUBERNETES_VERSION
valueFrom:
fieldRef:
fieldPath: metadata.annotations['certification.fileserver/kubernetes-version']
- name: CSI_DRIVER
valueFrom:
fieldRef:
fieldPath: metadata.annotations['certification.fileserver/csi-driver']
- name: STORAGE_CLASS
valueFrom:
fieldRef:
fieldPath: metadata.annotations['certification.fileserver/storage-class']
- name: ACCESS_MODE
value: ReadWriteOnce
command:
- /bin/bash
- -c
- |
set -euo pipefail
ROOT="${FILESERVER_STORAGE_ROOT}"
mkdir -p "${ROOT}/staging" "${ROOT}/content"
# Atomic rename within one file store is the property the publish path depends on.
echo probe > "${ROOT}/staging/probe"
if mv "${ROOT}/staging/probe" "${ROOT}/content/probe" 2>/dev/null; then
ATOMIC_MOVE=true
else
ATOMIC_MOVE=false
fi
# Same device means a rename is a metadata operation rather than a copy.
STAGING_DEV=$(stat -c %d "${ROOT}/staging")
CONTENT_DEV=$(stat -c %d "${ROOT}/content")
[ "${STAGING_DEV}" = "${CONTENT_DEV}" ] && SAME_STORE=true || SAME_STORE=false
# O_EXCL create is what makes a publish create-only rather than an overwrite.
if (set -o noclobber; echo x > "${ROOT}/content/excl") 2>/dev/null; then
ATOMIC_CREATE=true
else
ATOMIC_CREATE=false
fi
cat > "${ROOT}/certification-result.json" <<RESULT
{
"kubernetesVersion": "${KUBERNETES_VERSION:-unknown}",
"csiDriver": "${CSI_DRIVER:-unknown}",
"storageClass": "${STORAGE_CLASS:-unknown}",
"accessMode": "${ACCESS_MODE}",
"backend": "$(stat -f -c %T "${ROOT}")",
"mountOptions": "$(findmnt -no OPTIONS --target "${ROOT}" || echo unknown)",
"atomicMove": ${ATOMIC_MOVE},
"sameFileStore": ${SAME_STORE},
"atomicCreate": ${ATOMIC_CREATE}
}
RESULT
cat "${ROOT}/certification-result.json"
# Fail closed: a volume that cannot publish atomically must not be certified silently.
[ "${SAME_STORE}" = "true" ] || { echo "staging and content are on different stores"; exit 1; }
volumeMounts:
- name: storage
mountPath: /var/lib/backend/files
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
volumes:
- name: storage
persistentVolumeClaim:
claimName: fileserver-certification