diff --git a/apps/operator/src/controller/git_hook.rs b/apps/operator/src/controller/git_hook.rs index 312a59a..6918b8d 100644 --- a/apps/operator/src/controller/git_hook.rs +++ b/apps/operator/src/controller/git_hook.rs @@ -1,4 +1,4 @@ -//! Controller for the `GitHook` CRD — Deployment + ConfigMap. +//! Controller for the `GitHook` CRD — Deployment + ConfigMap + PVC. use crate::context::ReconcileState; use crate::controller::app::{apply_deployment, patch_status}; @@ -18,6 +18,10 @@ pub async fn reconcile(gh: Arc, ctx: Arc) -> Result<(), let labels = std_labels(); let cm_name = format!("{}-config", name); + // ---- PVC ---- + let pvc = build_pvc(ns, name, spec, &or, &labels); + apply_pvc(client, ns, &format!("{}-data", name), &pvc).await?; + // ---- ConfigMap ---- let configmap = build_configmap(ns, &cm_name, &or, &labels); apply_configmap(client, ns, &cm_name, &configmap).await?; @@ -78,13 +82,14 @@ fn build_deployment( }; let resources = super::app::build_resources(&spec.resources); - // Add WORKER_ID env + // Add WORKER_ID and APP_REPOS_ROOT env vars let worker_id = spec .worker_id .clone() .unwrap_or_else(|| uuid::Uuid::new_v4().to_string()); let mut env_vars: Vec = env.iter().map(env_var_to_json).collect(); env_vars.push(json!({ "name": "HOOK_POOL_WORKER_ID", "value": worker_id })); + env_vars.push(json!({ "name": "APP_REPOS_ROOT", "value": "/data/repos" })); json!({ "metadata": child_meta(name, ns, or, labels.clone()), @@ -100,12 +105,21 @@ fn build_deployment( "env": env_vars, "imagePullPolicy": pull, "resources": resources, - "volumeMounts": [{ "name": "hook-config", "mountPath": "/config" }] + "volumeMounts": [ + { "name": "hook-data", "mountPath": "/data" }, + { "name": "hook-config", "mountPath": "/config" } + ] }], - "volumes": [{ - "name": "hook-config", - "configMap": { "name": cm_name } - }] + "volumes": [ + { + "name": "hook-data", + "persistentVolumeClaim": { "claimName": format!("{}-data", name) } + }, + { + "name": "hook-config", + "configMap": { "name": cm_name } + } + ] } } } @@ -135,3 +149,41 @@ async fn apply_configmap( Err(e) => Err(e), } } + +fn build_pvc( + ns: &str, + name: &str, + spec: &GitHookSpec, + or: &crate::crd::OwnerReference, + labels: &std::collections::BTreeMap, +) -> Value { + json!({ + "metadata": child_meta(&format!("{}-data", name), ns, or, labels.clone()), + "spec": { + "accessModes": ["ReadWriteOnce"], + "resources": { "requests": { "storage": spec.storage_size } } + } + }) +} + +async fn apply_pvc( + client: &kube::Client, + ns: &str, + name: &str, + body: &Value, +) -> Result<(), kube::Error> { + let api: kube::Api = kube::Api::namespaced(client.clone(), ns); + let jr = JsonResource::new(Default::default(), body.clone()); + match api.get(name).await { + Ok(_) => { + // PVCs are immutable except for labels/annotations — skip update + Ok(()) + } + Err(kube::Error::Api(e)) if e.code == 404 => { + info!(name, ns, "creating git-hook PVC"); + let _ = api.create(&kube::api::PostParams::default(), &jr).await?; + Ok(()) + } + Err(e) => Err(e), + } +} diff --git a/apps/operator/src/crd.rs b/apps/operator/src/crd.rs index 518b3d9..aebc9b7 100644 --- a/apps/operator/src/crd.rs +++ b/apps/operator/src/crd.rs @@ -407,11 +407,16 @@ pub struct GitHookSpec { pub image_pull_policy: String, #[serde(skip_serializing_if = "Option::is_none")] pub worker_id: Option, + #[serde(default = "default_githook_storage_size")] + pub storage_size: String, } fn default_githook_image() -> String { "myapp/git-hook:latest".to_string() } +fn default_githook_storage_size() -> String { + "10Gi".to_string() +} #[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct GitHookStatus { diff --git a/deploy/crd/git-hook-crd.yaml b/deploy/crd/git-hook-crd.yaml index 7123c85..e9e0632 100644 --- a/deploy/crd/git-hook-crd.yaml +++ b/deploy/crd/git-hook-crd.yaml @@ -87,6 +87,9 @@ spec: default: IfNotPresent workerId: type: string + storageSize: + type: string + default: 10Gi status: type: object properties: