Snapshot of the in-flight state that already existed, identically, in both this worktree and the main checkout before this session began: the initial HTTP Client platform implementation (previously untracked), the redis-lab removal, and the JPA / object-storage / notification integration work. Kept separate from this session's HTTP Client review response, which lands in the following commit, so the two bodies of work stay reviewable apart. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
128 lines
4.7 KiB
YAML
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
|