summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Spek <p.spek@tyil.nl>2023-10-25 10:20:40 +0200
committerPatrick Spek <p.spek@tyil.nl>2023-10-25 10:20:40 +0200
commitb4ae9ec12f530ff445a4d253735193c42f821b2d (patch)
tree7e5517b4ee4ff4e4df387146cfdc82493d0470da
parent6ba3dec1e3b5fe5e5f81fa5c273caa9d2ac4309c (diff)
First draft that seems to work
-rw-r--r--nfs_operator.py141
-rw-r--r--requirements.txt2
2 files changed, 103 insertions, 40 deletions
diff --git a/nfs_operator.py b/nfs_operator.py
index 321a37c..d1e12d0 100644
--- a/nfs_operator.py
+++ b/nfs_operator.py
@@ -4,19 +4,29 @@ import kopf
import kubernetes
import os
import pathlib
+import shutil
+import subprocess
import uuid
-# Create the configuration dict
-configuration = { }
-
@kopf.on.startup()
def init(logger, memo: kopf.Memo, **kwargs):
# Set the configuration
memo.config = {
- "server": os.environ.get("X_NFS_OPERATOR_SERVER_ADDRESS", "127.0.0.1"),
"basedir": os.environ.get("X_NFS_OPERATOR_BASEDIR", "/var/nfs"),
- "pvcPrefix": os.environ.get("X_NFS_OPERATOR_PREFIX", "pvc-"),
+ "cleanup": os.environ.get("X_NFS_OPERATOR_CLEANUP", False),
+ "exportsCidr": os.environ.get("X_NFS_OPERATOR_EXPORT_CIDR", "0.0.0.0/0"),
"name": os.environ.get("X_NFS_OPERATOR_NAME", "nfs-provisioner"),
+ "nfsExportsFile": os.environ.get("X_NFS_OPERATOR_NFS_EXPORTS_FILE", "/etc/exports.d/nfs-operator.exports"),
+ "nfsOptions": [
+ "crossmnt",
+ "no_all_squash",
+ "no_root_squash",
+ "no_subtree_check",
+ "rw",
+ "sec=sys",
+ ],
+ "pvcPrefix": os.environ.get("X_NFS_OPERATOR_PREFIX", "pvc-"),
+ "server": os.environ.get("X_NFS_OPERATOR_SERVER_ADDRESS", "127.0.0.1"),
"storageClassName": os.environ.get("X_NFS_OPERATOR_STORAGE_CLASS_NAME", "nfs"),
}
@@ -29,59 +39,110 @@ def create_pv(spec, name, namespace, annotations, logger, memo: kopf.Memo, **kwa
return
# Generate some names and paths for later use
- volume = uuid.uuid4()
- pvc_name = memo["config"]["pvcPrefix"] + str(volume)
+ fsid = str(uuid.uuid4())
+ pvc_name = memo["config"]["pvcPrefix"] + fsid
pvc_path = pathlib.Path(annotations.get("nfs-operator.tyil.nl/basedir", memo["config"]["basedir"]) + "/" + pvc_name)
+ nfs_export = annotations.get("nfs-operator.tyil.nl/export-cidr", memo["config"]["exportsCidr"])
+ nfs_opts = ",".join(memo["config"]["nfsOptions"] + ["fsid=" + fsid])
# Create NFS share
logger.info(f"Creating directory at {pvc_path}")
pvc_path.mkdir(parents = True)
+ logger.info(f"Adding export for {pvc_path}")
+ with open(memo["config"]["nfsExportsFile"], "a") as fh:
+ fh.write(f"{pvc_path} {memo['config']['exportsCidr']}({nfs_opts})\n")
+
+ # Reload NFS exports
+ logger.info("Reloading NFS exports")
+ subprocess.run(["exportfs", "-ra"])
+
# Connect to the Kubernetes API
k8s = kubernetes.client.CoreV1Api()
# Create PV
logger.info(f"Creating PersistentVolume {pvc_name}")
- #k8s.create_persistent_volume({
- # "metadata": {
- # "name": name,
- # "labels": {
- # "app.kubernetes.io/managed-by": configuration["name"],
- # "app.kubernetes.io/name": name,
- # "pv.kubernetes.io/provisioned-by": configuration["name"],
- # },
- # },
- # "spec": {
- # "accessModes": spec.get("accessModes"),
- # "capacity": {
- # "storage": spec.get("resources.requests.storage"),
- # },
- # "storageClassName": spec.get("storageClassName"),
- # "volumeMode": "Filesystem",
- # "persistentVolumeReclaimPolicy": "Delete",
- # "nfs": {
- # "path": pvc_path,
- # "server": memo["config"]["server"],
- # },
- # "mountOptions": [
- # "hard",
- # ],
- # },
- #})
-
- # Update PVC
- logger.info(f"Updating PersistentVolumeClaim {namespace}/{name}")
- k8s.patch_namespaced_persistent_volume_claim(name, namespace, {
+ k8s.create_persistent_volume({
+ "metadata": {
+ "name": pvc_name,
+ "labels": {
+ "app.kubernetes.io/managed-by": memo["config"]["name"],
+ "app.kubernetes.io/name": pvc_name,
+ "pv.kubernetes.io/provisioned-by": memo["config"]["name"],
+ },
+ },
"spec": {
+ "accessModes": spec.get("accessModes"),
+ "capacity": {
+ "storage": spec.get("resources")["requests"]["storage"],
+ },
+ "storageClassName": spec.get("storageClassName"),
"volumeMode": "Filesystem",
- "volumeName": pvc_name,
+ "persistentVolumeReclaimPolicy": "Delete",
+ "nfs": {
+ "path": str(pvc_path.absolute()),
+ "server": memo["config"]["server"],
+ },
+ "mountOptions": [
+ "hard",
+ ],
},
})
+ # Update PVC
+ logger.info(f"Updating PersistentVolumeClaim {namespace}/{name}")
+ k8s.patch_namespaced_persistent_volume_claim(name, namespace, [
+ {
+ "op": "add",
+ "path": "/metadata/annotations",
+ "value": {
+ "nfs-operator.tyil.nl/export-path": str(pvc_path.absolute()),
+ "nfs-operator.tyil.nl/export-cidr": nfs_export,
+ },
+ },
+ ])
+
@kopf.on.update("persistentvolumeclaim")
def update_pv(logger, **kwargs):
logger.debug("PVC updates are NYI")
@kopf.on.delete("persistentvolumeclaim")
-def delete_pv(logger, **kwargs):
- logger.debug("Deleting the thing...")
+def delete_pv(spec, annotations, logger, memo: kopf.Memo, **kwargs):
+ # Check whether the PVC has the correct storageClassName
+ if spec.get("storageClassName") != memo["config"]["storageClassName"]:
+ logger.debug("Not handling storageClassName '" + spec.get("storageClassName") + "'")
+ return
+
+ # Generate some names and paths for later use
+ pv_name = spec.get("volumeName")
+ pv_path = pathlib.Path(annotations.get("nfs-operator.tyil.nl/export-path"))
+
+ # Connect to the Kubernetes API
+ k8s = kubernetes.client.CoreV1Api()
+
+ # Delete the PV
+ logger.info(f"Deleting PV {pv_name}")
+ k8s.delete_persistent_volume(pv_name)
+
+ # Clean up directory
+ if memo["config"]["cleanup"]:
+ logger.info(f"Cleaning up {pv_path}")
+ shutil.rmtree(pv_path)
+
+ # Clean up NFS export
+ logger.info(f"Removing export for {pv_path}")
+ exports = []
+
+ with open(memo["config"]["nfsExportsFile"], "r") as fh:
+ exports = fh.readlines()
+
+ with open(memo["config"]["nfsExportsFile"], "w") as fh:
+ for export in exports:
+ if export.startswith(str(pv_path) + " "):
+ continue
+
+ fh.write(export)
+
+ # Reload NFS exports
+ logger.info("Reloading NFS exports")
+ subprocess.run(["exportfs", "-ra"])
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..d1f5d64
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,2 @@
+kopf
+kubernetes