{"id":28480509,"url":"https://github.com/statcan/volume-cleaner","last_synced_at":"2026-05-09T00:02:12.540Z","repository":{"id":294518291,"uuid":"987083625","full_name":"StatCan/volume-cleaner","owner":"StatCan","description":"A Kubernetes CronJob that automatically identifies and cleans up stale Persistent Volume Claims and Persistent Volumes (K8S) linked to an associated Azure disk.","archived":false,"fork":false,"pushed_at":"2025-06-26T19:25:43.000Z","size":224,"stargazers_count":1,"open_issues_count":15,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-06-26T19:26:01.107Z","etag":null,"topics":["azure","government","kubernetes","persistent-volume-claims","persistent-volumes"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/StatCan.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-05-20T14:49:29.000Z","updated_at":"2025-06-25T20:01:34.000Z","dependencies_parsed_at":"2025-06-11T21:44:58.787Z","dependency_job_id":null,"html_url":"https://github.com/StatCan/volume-cleaner","commit_stats":null,"previous_names":["statcan/volume-cleaner"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/StatCan/volume-cleaner","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/StatCan%2Fvolume-cleaner","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/StatCan%2Fvolume-cleaner/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/StatCan%2Fvolume-cleaner/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/StatCan%2Fvolume-cleaner/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/StatCan","download_url":"https://codeload.github.com/StatCan/volume-cleaner/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/StatCan%2Fvolume-cleaner/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":263388666,"owners_count":23459246,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["azure","government","kubernetes","persistent-volume-claims","persistent-volumes"],"created_at":"2025-06-07T19:06:38.047Z","updated_at":"2026-05-09T00:02:12.530Z","avatar_url":"https://github.com/StatCan.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Kubernetes Volume Cleaner\n\n([Français](#volume-cleaner-kubernetes))\n\n\u003cp align=\"center\"\u003e\n    \u003cimg src=\"./assets/volume_cleaner.jpg\" alt=\"Volume Cleaner Logo\" width=\"400\"/\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://deepwiki.com/StatCan/volume-cleaner\"\u003e\n    \u003cimg src=\"https://deepwiki.com/badge.svg\" alt=\"Ask DeepWiki\"/\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://www.gnu.org/licenses/agpl-3.0\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/License-AGPL_v3-blue.svg\" alt=\"License: AGPL v3\"/\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\nA Kubernetes CronJob that automatically identifies and cleans up unused Persistent Volume Claims and Persistent Volumes (K8S).\n\n## Contents\n\n- [Introduction](#introduction)\n- [Requirements](#requirements)\n- [Features](#features)\n- [Quick Start](#quick-start)\n- [How to Contribute](#how-to-contribute)\n- [Code of Conduct](#code-of-conduct)\n- [License](#license)\n\n\n## Introduction \n\nThis project was designed to integrate with Statistic Canada’s [The Zone](https://zone.pages.cloud.statcan.ca/docs/en/) platform. In The Zone, users can create individual workspaces with Kubeflow Notebooks. To persist their work, users can attach volumes to their notebooks. These volumes are not automatically deleted when a notebook is deleted. Users are free to move them or to reuse them. As a result, over time, users tend to amass several unused volumes. These volumes sit on the cloud and result in unnecessary costs. The purpose of this project is to design an automatic system that detects these unused volumes and safely removes them, reducing unnecessary expenditure. \n\nDespite being primarily designed for Statistics Canada, this project strongly values open-source practices. The codebase is available for public use and the entire development process is documented on the [issues](https://github.com/StatCan/volume-cleaner/issues) page. The volume cleaner was designed to have as little coupling as possible so it can easily integrate into projects outside Statistics Canada. Using open-source and being open-source was an important development philosophy.\n\n### Architectural Structure\n\n**DevOps Build Process**\n\u003cimg width=\"3005\" height=\"1836\" alt=\"DevOps Build Process Diagram\" src=\"https://github.com/user-attachments/assets/07d69a08-f164-4f17-b83f-2bb5fe66fa4a\" /\u003e\n\n**In-Cluster Running Process**\n\u003cimg width=\"2692\" height=\"1755\" alt=\"Volume Cleaner Architectural Diagram\" src=\"https://github.com/user-attachments/assets/7c91f806-941c-47c9-8417-e58fe4827367\" /\u003e\n\n## Requirements\n\n- Golang 1.24.3 \n\n- Kubernetes (Kubernetes API v0.33.1)\n\n- Azure Container Registry (or some other container registry)\n\n- GC Notify Email Service (Email Template ID + API Key) (or some other 3rd party email service)\n\n- The Testify Testing Framework \n\n## Features \n\n- **🔍 Automatic PVC Discovery** : Scans Kubeflow namespaces to identify unattached Persistent Volume Claims that are no longer associated with StatefulSets\n\n- **⏰ Real-time Monitoring** : Continuously watches StatefulSet lifecycle events to automatically label/unlabel PVCs when they become attached or detached\n\n- **🏷️ Intelligent Labeling System** : Automatically applies timestamped labels to unattached PVCs for tracking staleness and cleanup eligibility\n\n- **📧 Email Notifications** : Sends automated warning emails to namespace owners at configurable intervals before PVC deletion\n\n- **⚡ Configurable Grace Periods** : Supports customizable grace periods (minimum 1 day) before stale PVCs are eligible for deletion\n\n- **📅 Flexible Notification Scheduling** : Allows configuration of multiple notification times (e.g., 1, 2, 3, 7, 30 days before deletion)\n\n- **🔄 Dual-Component Architecture** : Separates continuous monitoring (controller) from periodic cleanup operations (scheduler) for optimal resource usage\n\n- **🧪 Comprehensive Testing** : Features extensive unit tests for all core functionality including PVC discovery, labeling, and cleanup logic\n\nThe system operates through two main components: a controller that runs continuously to monitor StatefulSet events and label PVCs, and a scheduler that runs periodically (via CronJob) to perform cleanup operations and send notifications. The project integrates with GC Notify for email services and supports Azure disk cleanup in Kubernetes environments.\n\n## Quick Start\n\n### Prerequisites\n\nSet your Kubernetes context to the target cluster:\n\n```bash\n\nkubectl config use-context \u003cyour-cluster-context\u003e\n\n```\n\n### Architecture Overview\n\nThe volume-cleaner is split into 2 main components:\n\n- **Controller:** Continuously monitors StatefulSet events and labels unattached PVCs with timestamps\n- **Scheduler:** Runs periodically to find PVCs past their stale date, sends email notifications, and deletes expired volumes\n\n### Installation\n\n1. Clone this repository:\n\n```bash\n\ngit clone https://github.com/StatCan/volume-cleaner.git  \ncd volume-cleaner\n\n```\n\n2. Customize the behavior of the Controller in `manifests/controller/controller_config.yaml`\n\n   * `NAMESPACE`: Target namespace to monitor (e.g., \"kubeflow-profile\"). Leave this value as an empty string to scan all namespaces\n   * `TIME_LABEL`: Label key for storing unattached timestamp (e.g.: \"volume-cleaner/unattached-time\") \n   * `NOTIF_LABEL`: Label key for notification count tracking (e.g.: \"volume-cleaner/notification-count\")\n   * `TIME_FORMAT`: Timestamp format for labels (e.g: \"2006-01-02_15-04-05Z\")\n   * `STORAGE_CLASSES`: Comma-separated list of target storage classes to filter by (e.g., \"standard\")\n   * `RESET_RUN`: Set to \"true\" to remove all volume cleaner related labels from cluster before starting\n\n3. Customize the behavior of the Scheduler in `manifests/scheduler/scheduler_config.yaml` \n\n   * `NAMESPACE`: Target namespace to scan for unused PVCs, leave this value as an empty string to scan all namespaces \n   * `TIME_LABEL`: Must match controller's time label\n   * `NOTIF_LABEL`: Must match controller's notification label\n   * `IGNORE_LABEL`: If this label is true on a PVC, the scheduler will skip it (e.g.: \"volume-cleaner/ignore\")\n   * `GRACE_PERIOD`: Days before PVC deletion (e.g., \"180\") \n   * `TIME_FORMAT`: Must match controller's time format\n   * `DRY_RUN`: Set to \"true\" for testing without actual deletion \n   * `NOTIF_TIMES`: Comma-separated days before deletion to send notifications (e.g., \"1, 2, 3, 4, 7, 30\")\n   * `BASE_URL`: GC Notify API base URL \n   * `ENDPOINT`: Email notification endpoint \n\n4. Set Secrets in `manifests/scheduler/scheduler_secret.yaml` \n\n   * `EMAIL_TEMPLATE_ID`: GC notify email template ID \n   * `API_KEY`: GC Notify API authentication key, do not push API keys to this repository\n  \n5. If you're building the image yourself, configure the pull target in `manifests/controller/controller_deployment.yaml` and `manifests/scheduler/scheduler_job.yaml`. \n   E.g `image: docker.io/statcan/volume-cleaner-controller:latest`\n\n6. Run the controller \u0026 scheduler using the Make command\n\n```bash\nmake run_controller\nmake run_scheduler\n```\n\n7. Remove the scheduler and controller (optional)\n\n```bash\nmake clean \n```\n8. Trigger scheduler run immediately (optional)\n```bash\nkubectl create job volume-cleaner-scheduler --from=cronjob/volume-cleaner-scheduler -n ${JOB_NAMESPACE_HERE}\n```\nRead [this](https://github.com/StatCan/volume-cleaner/blob/main/docs/project_outline.docx) document for more information.\n\n## How to Contribute\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md)\n\n## Code of Conduct\n\nSee [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md)\n\n## License\n\nVolume Cleaner is distributed under [AGPL-3.0.only](LICENSE.md).\n\n### Open Source at Government of Canada\n\n- [Using Open Source Software](https://www.canada.ca/en/government/system/digital-government/digital-government-innovations/open-source-software/guide-for-using-open-source-software.html)\n\n- [Publishing Open Source Code](https://www.canada.ca/en/government/system/digital-government/digital-government-innovations/open-source-software/guide-for-publishing-open-source-code.html)\n\n- [Contributing to Open Source Software](https://www.canada.ca/en/government/system/digital-government/digital-government-innovations/open-source-software/guide-for-contributing-to-open-source-software.html)\n\n______________________\n\n# Volume Cleaner Kubernetes\n\n\u003cp align=\"center\"\u003e\n    \u003cimg src=\"./assets/volume_cleaner.jpg\" alt=\"Volume Cleaner Logo\" width=\"400\"/\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://deepwiki.com/StatCan/volume-cleaner\"\u003e\n    \u003cimg src=\"https://deepwiki.com/badge.svg\" alt=\"Ask DeepWiki\"/\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://www.gnu.org/licenses/agpl-3.0\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/License-AGPL_v3-blue.svg\" alt=\"License: AGPL v3\"/\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\nUn CronJob Kubernetes qui identifie automatiquement et nettoie les Persistent Volume Claims et Persistent Volumes (K8S).\n\n## Contenu\n\n- [Introduction](#introduction)\n- [Prérequis](#prérequis)\n- [Fonctionnalités](#fonctionnalités)\n- [Démarrage rapide](#démarrage-rapide)\n- [Comment contribuer](#comment-contribuer)\n- [Code de conduite](#code-de-conduite)\n- [Licence](#licence)\n\n## Introduction\n\nCe projet a été conçu pour s'intégrer à la plateforme [The Zone](https://zone.pages.cloud.statcan.ca/docs/en/) de Statistique Canada. Dans The Zone, les utilisateurs peuvent créer des espaces de travail individuels avec des notebooks Kubeflow. Pour conserver leur travail, ils peuvent associer des volumes à leurs notebooks. Ces volumes ne sont pas supprimés automatiquement lors de la suppression d'un notebook. Les utilisateurs peuvent les déplacer ou les réutiliser. En conséquence, au fil du temps, de nombreux volumes inutilisés s'accumulent, entraînant des coûts cloud superflus. L'objectif de ce projet est de mettre en place un système automatique qui détecte ces volumes inutilisés et les supprime en toute sécurité, réduisant ainsi les dépenses inutiles.\n\nBien qu'il soit principalement conçu pour Statistique Canada, ce projet valorise fortement les pratiques de sources ouvertes. Le code est disponible publiquement et tout le processus de développement est documenté sur la page des [issues](https://github.com/StatCan/volume-cleaner/issues). Le nettoyeur de volumes a été conçu pour avoir un couplage minimal afin de pouvoir s'intégrer facilement dans d'autres projets. L'utilisation et la contribution en sources ouvertes ont été des principes directeurs importants.\n\n### Structure architecturale\n\n**Processus de build DevOps**\n\u003cimg width=\"3005\" height=\"1836\" alt=\"Processus de build DevOps du volume-cleaner\" src=\"https://github.com/user-attachments/assets/07d69a08-f164-4f17-b83f-2bb5fe66fa4a\" /\u003e\n\n**Processus en cours d’exécution dans le cluster**\n\u003cimg width=\"2692\" height=\"1755\" alt=\"Schéma d'architecture du volume-cleaner\" src=\"https://github.com/user-attachments/assets/7c91f806-941c-47c9-8417-e58fe4827367\" /\u003e\n\n## Prérequis\n\n- Golang 1.24.3\n\n- Kubernetes (API Kubernetes v0.33.1)\n\n- Azure Container Registry (ou autre registry de conteneurs)\n\n- Service d'envoi d'e-mails GC Notify (Email Template ID + API Key) (ou autre service tiers)\n\n- Testify's Testing Framework \n\n## Fonctionnalités\n\n- **🔍 Découverte automatique de PVC** : Scanne les espaces de noms Kubeflow pour identifier les Persistent Volume Claims non attachés n'étant plus associés à des StatefulSets.\n\n- **⏰ Surveillance en temps réel** : Observe continuellement les événements de cycle de vie des StatefulSets pour étiqueter ou retirer l'étiquette des PVC lorsqu'ils sont attachés ou détachés.\n\n- **🏷️ Système d'étiquetage intelligent** : Applique automatiquement des étiquettes horodatées aux PVC non attachés pour suivre leur ancienneté et leur éligibilité au nettoyage.\n\n- **📧 Notifications par e-mail** : Envoie des e-mails d'avertissement automatisés aux propriétaires de namespace à des intervalles configurables avant la suppression des PVC.\n\n- **⚡ Délais de grâce configurables** : Prend en charge des délais de grâce personnalisables (minimum 1 jour) avant que les PVC obsolètes ne soient éligibles à la suppression.\n\n- **📅 Planification souple des notifications** : Permet de configurer plusieurs délais de notification (par exemple, 1, 2, 3, 7, 30 jours avant la suppression).\n\n- **🔄 Architecture à deux composants** : Sépare la surveillance continue (contrôleur) des opérations de nettoyage périodiques (planificateur) pour une utilisation optimale des ressources.\n\n- **🧪 Tests complets** : Inclut de nombreux tests unitaires pour toutes les fonctionnalités principales, notamment la découverte, l'étiquetage et la logique de nettoyage des PVC.\n\nLe système fonctionne via deux composants principaux : un contrôleur qui s'exécute en continu pour surveiller les événements des StatefulSets et étiqueter les PVC, et un planificateur qui s'exécute périodiquement (via CronJob) pour effectuer les opérations de nettoyage et envoyer les notifications. Le projet s'intègre à GC Notify pour le service d'e-mails et prend en charge le nettoyage des disques Azure dans les environnements Kubernetes.\n\n## Démarrage rapide\n\n### Prérequis\n\nPlacez votre contexte Kubernetes sur le cluster cible :\n\n```bash\nkubectl config use-context \u003cvotre-cluster-context\u003e\n````\n\n### Vue d’ensemble de l’architecture\n\nLe volume-cleaner est composé de 2 composants principaux :\n\n* **Contrôleur :** Surveille en continu les événements de StatefulSet et étiquette les PVC non attachés avec un horodatage\n* **Planificateur :** S’exécute périodiquement pour repérer les PVC dépassant leur date de péremption, envoyer des notifications par e‑mail et supprimer les volumes expirés\n\n### Installation\n\n1. Clonez ce dépôt :\n\n   ```bash\n   git clone https://github.com/StatCan/volume-cleaner.git  \n   cd volume-cleaner\n   ```\n\n2. Personnalisez le comportement du Contrôleur dans `manifests/controller/controller_config.yaml` :\n\n   * `NAMESPACE` : Espace de noms à surveiller (par ex. “kubeflow-profile”); laissez cette valeur vide pour scanner tous les espaces de noms\n   * `TIME_LABEL` : Clé de l'étiquette pour stocker l’horodatage des PVC non attachés (par ex. `volume-cleaner/unattached-time`)\n   * `NOTIF_LABEL` : Clé de l'étiquette pour le suivi du nombre de notifications (par ex. `volume-cleaner/notification-count`)\n   * `TIME_FORMAT` : Format de l’horodatage pour les étiquettes (par défaut : `2006-01-02_15-04-05Z`)\n   * `STORAGE_CLASSES` : Liste des classes de stockage cibles à filtrer, séparée par des virgules (p. ex. \"standard\")\n   * `RESET_RUN` : Définir sur 'true' pour retirer tous les étiquettes liés au nettoyeur de volumes du cluster avant le démarrage.\n\n3. Personnalisez le comportement du Planificateur dans `manifests/scheduler/scheduler_config.yaml` :\n\n   * `NAMESPACE` : Espace de noms à scanner pour les PVC périmés; laissez cette valeur vide pour scanner tous les espaces de noms\n   * `TIME_LABEL` : Doit correspondre au `TIME_LABEL` du contrôleur\n   * `NOTIF_LABEL` : Doit correspondre au `NOTIF_LABEL` du contrôleur\n   * `IGNORE_LABEL` : Si cette étiquette est définie à true sur un PVC, le planificateur l’ignorera (par exemple : `volume-cleaner/ignore`).\n   * `GRACE_PERIOD` : Nombre de jours avant suppression du PVC (par ex. `\"180\"`)\n   * `TIME_FORMAT` : Doit correspondre au `TIME_FORMAT` du contrôleur\n   * `DRY_RUN` : À `\"true\"` pour tester sans suppression réelle\n   * `NOTIF_TIMES` : Jours avant suppression pour envoyer des notifications (par ex. `\"1,2,3,4,7,30\"`)\n   * `BASE_URL` : URL de base de l’API GC Notify\n   * `ENDPOINT` : Point de terminaison pour l’envoi des e‑mails\n\n4. Définissez les Secrets dans `manifests/scheduler/scheduler_secret.yaml` :\n\n   * `EMAIL_TEMPLATE_ID` : ID du modèle d’e‑mail GC Notify\n   * `API_KEY` : Clé d’authentification GC Notify, ne pas pousser les clés API dans ce dépôt\n\n5. Si vous construisez l'image vous-même, configurez la cible d'extraction dans `manifests/controller/controller_deployment.yaml` et `manifests/scheduler/scheduler_job.yaml`.\nEx. `image: docker.io/statcan/volume-cleaner-controller:latest`.\n\n6. Lancez le Contrôleur et le Planificateur avec Make :\n\n   ```bash\n   make run_controller\n   make run_scheduler\n   ```\n\n7. Supprimez le planificateur et le contrôleur (optionnel) :\n\n   ```bash\n   make clean\n   ```\n8. Déclencher immédiatement l'exécution du planificateur (optionnel)\n```bash\nkubectl create job volume-cleaner-scheduler --from=cronjob/volume-cleaner-scheduler -n ${NOM_ESPACE_DE_NOMS_ICI}\n```\nLisez [ce](https://github.com/StatCan/volume-cleaner/blob/main/docs/project_outline.docx) document pour plus d'informations (version en anglais seulement).\n\n## Comment contribuer\n\nConsultez [CONTRIBUTING.md](CONTRIBUTING.md)\n\n## Code de conduite\n\nConsultez [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md)\n\n## Licence\n\nVolume Cleaner est distribué sous [AGPL-3.0.only](LICENSE.md).\n\n### Open Source au sein du Gouvernement du Canada\n\n- [Utilisation de logiciels Open Source](https://www.canada.ca/fr/gouvernement/systeme/gouvernement-numerique/innovations-gouvernementales-numeriques/logiciels-libres/guide-pour-lutilisation-de-logiciels-libres.html)\n- [Publication de code Open Source](https://www.canada.ca/fr/gouvernement/systeme/gouvernement-numerique/innovations-gouvernementales-numeriques/logiciels-libres/guide-pour-la-publication-du-code-source-libre.html)\n- [Contribution aux logiciels Open Source](https://www.canada.ca/fr/gouvernement/systeme/gouvernement-numerique/innovations-gouvernementales-numeriques/logiciels-libres/guide-de-contribution-aux-logiciels-libres.html)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstatcan%2Fvolume-cleaner","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstatcan%2Fvolume-cleaner","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstatcan%2Fvolume-cleaner/lists"}