Skip to content

Commit ecaf3bb

Browse files
authored
Merge pull request #2 from Cosmo-Tech/ggon_fixes
Ggon fixes
2 parents 0c299ca + a6bc3d0 commit ecaf3bb

9 files changed

Lines changed: 333 additions & 289 deletions

File tree

README.md

Lines changed: 79 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,9 @@
5555
* `backup-map` ConfigMap
5656
> Note: this ConfigMap is used to orchestrate the backup. \
5757
> You can manually edit it to customize the backup behaviors, the command below is just a quick way to create it. \
58-
> If it already exists, you can read it with `python -m src.master -c KUBECTL_CONTEXT -gm` or `kubectl get cm clustersnap-backup-map`
58+
> If it already exists, you can read it with `python -m src.master -c KUBECTL_CONTEXT -bm -g` or `kubectl get cm clustersnap-backup-map`
5959
```
60-
python -m src.master -c KUBECTL_CONTEXT -rm
60+
python -m src.master -c KUBECTL_CONTEXT -bm
6161
```
6262
6363
@@ -94,13 +94,13 @@ python -m src.master --help
9494
* `importer.py` => entrypoint of the importer pod (import backups from remote storage to Kubernetes)
9595
9696
97-
### The `backup-map`
97+
### ConfigMap `backup-map`
9898
Clustersnap relies on a ConfigMap called the `backup-map` to orchestrate its backups plans.
9999
100100
The `backup-map` is really simple:
101-
* consists of listing workloads (Deployments/StatefulSets) and their associated PVC if any.
102-
* must be generated before running Clustersnap jobs.
103-
* can be manually modified (and it's even recommanded to fit with your needs)
101+
* consists of listing workloads (Deployments/StatefulSets) and their associated PVC if any
102+
* must be generated before running Clustersnap jobs
103+
* can/must be manually modified => The backup-map is pre-gen but some informations are not predictable, any "CHANGEME" values must be replaced
104104
* stored in namespace of the `Clusternap master pod` (or default namespace if Clustersnap is not running in the cluster)
105105
106106
@@ -110,19 +110,10 @@ clustersnap-backup-map.yaml: |
110110
cluster: example
111111
namespaces:
112112
- name: namespace1
113-
workloads: []
114113
claims: []
115114
- name: namespace2
116-
workloads: []
117115
claims: []
118116
- name: namespace3
119-
workloads:
120-
- resource: Deployment/workload1
121-
replicas: 1
122-
- resource: StatefulSet/workload2
123-
replicas: 9
124-
- resource: StatefulSet/postgresql
125-
replicas: 1
126117
claims:
127118
- name: pvc-workload1
128119
backup-type: volume
@@ -147,47 +138,97 @@ clustersnap-backup-map.yaml: |
147138
- container: postgresql
148139
path: /data
149140
resource: StatefulSet/postgresql
141+
postgresql:
142+
port: 5432
143+
user: postgres
144+
password-secret:
145+
name: postgresql-config
146+
key: postgres-password
150147
```
151148
152149
153150
#### Tweaks
154-
Everything in the backup-map can be tweaked Here is the list of configurable keys:
155-
156-
| key | value | info
157-
|-------------------------------------------|-----------------|------
158-
| **`claims[]`** | *list* | List of PersistentVolumeClaims (PVCs) to orchestrate within the namespace. If empty = nothing to backup.
159-
| **`claims[].attachment-node`** | *string* | Name of the Kubernetes node where the PVC is attached. Useful if storage is node-constrained.
160-
| **`claims[].mounts[].path`** | *string* | Container mount path where data to back up is located.
161-
| **`claims[].backup-type`** | **`volume`** | The backup job will create a `.tar.gz containing data of the mounted paths` found in the PVC
162-
| **`claims[].backup-type`** | **`pg_dump`** | The backup job will use `pg_dump to create a .sql` dump of all the databases found in the PVC
163-
| **`claims[].postgresql`** | *dictionary* | Configuration block for PostgreSQL database access.
164-
| **`claims[].postgresql.host`** | *string* | Hardcoded PostgreSQL host/IP *(Overrides `host-env`)*. *Default: Dynamic Pod IP.*
165-
| **`claims[].postgresql.host-env`** | *string* | Name of the workload environment variable containing the host from original database pod.
166-
| **`claims[].postgresql.port`** | *integer* | Hardcoded connection port *(Overrides `port-env`)*. *Default: 5432.*
167-
| **`claims[].postgresql.port-env`** | *string* | Name of the workload environment variable containing the port from original database pod.
168-
| **`claims[].postgresql.user`** | *string* | Hardcoded PostgreSQL username *(Overrides `user-env`)*.
169-
| **`claims[].postgresql.user-env`** | *string* | Name of the workload environment variable containing the user from original database pod.
170-
| **`claims[].postgresql.password-env`** | *string* | **[REQUIRED]** Name of the workload environment variable containing the password (or mounted secret file).
171-
| **`claims[].postgresql.db`** | *string* | Hardcoded target database name *(Overrides `db-env`)*.
172-
| **`claims[].postgresql.db-env`** | *string* | Name of the workload environment variable containing the database name.
151+
Everything in the backup-map can be tweaked. \
152+
Here is the list of configurable keys:
153+
154+
| key | value | info
155+
|-------------------------------------------------------|-----------------|------
156+
| **`claims[]`** | *list* | List of PersistentVolumeClaims (PVCs) to orchestrate within the namespace. If empty = nothing to backup.
157+
| **`claims[].attachment-node`** | *string* | Name of the Kubernetes node where the PVC is attached. Useful if storage is node-constrained.
158+
| **`claims[].mounts[].path`** | *string* | Container mount path where data to back up is located.
159+
| **`claims[].backup-type`** | **`volume`** | The backup job will create a `.tar.gz containing data of the mounted paths` found in the PVC.
160+
| **`claims[].backup-type`** | **`pg_dump`** | The backup job will use `pg_dump to create a .sql` dump of all the databases found in the PVC.
161+
| **`claims[].postgresql`** | *dictionary* | Configuration block for PostgreSQL database access.
162+
| **`claims[].postgresql.host`** | *string* | Hardcoded PostgreSQL host/IP. *Default: Dynamic Pod IP.*
163+
| **`claims[].postgresql.port`** | *integer* | Hardcoded connection port. *Default: 5432.*
164+
| **`claims[].postgresql.user`** | *string* | Hardcoded PostgreSQL username *(Overrides `user-secret`)*. *Default: postgres.*
165+
| **`claims[].postgresql.user-secret`** | *dictionary* | Secret reference for username.
166+
| **`claims[].postgresql.user-secret.name`** | *string* | Name of the target Kubernetes Secret.
167+
| **`claims[].postgresql.user-secret.key`** | *string* | Data key inside the Secret holding the username value.
168+
| **`claims[].postgresql.user-secret.namespace`** | *string* | *(Optional)* Target Secret namespace if different from current PVC namespace.
169+
| **`claims[].postgresql.password-secret`** | *dictionary* | **[REQUIRED]** Secret reference for password.
170+
| **`claims[].postgresql.password-secret.name`** | *string* | Name of the target Kubernetes Secret.
171+
| **`claims[].postgresql.password-secret.key`** | *string* | Data key inside the Secret holding the password value.
172+
| **`claims[].postgresql.password-secret.namespace`** | *string* | *(Optional)* Target Secret namespace if different from current PVC namespace.
173+
| **`claims[].postgresql.db`** | *string* | Hardcoded target database name *(Overrides `db-secret`)*.
174+
| **`claims[].postgresql.db-secret`** | *dictionary* | Secret reference for database name.
175+
| **`claims[].postgresql.db-secret.name`** | *string* | Name of the target Kubernetes Secret.
176+
| **`claims[].postgresql.db-secret.key`** | *string* | Data key inside the Secret holding the database name value.
177+
| **`claims[].postgresql.db-secret.namespace`** | *string* | *(Optional)* Target Secret namespace if different from current PVC namespace.
173178
174179
> [!CAUTION]
175-
> **Security Warning:** The plain-text `password` key is **strictly forbidden** inside the `backup-map` to prevent plain-text credential leaks in a ConfigMap. You **must** use `password-env` to reference your workload's environment variable.
180+
> **Security Note:** Plain-text `password` keys are strictly forbidden in `backup-map`. Always use `password-secret`.
176181
177182
183+
184+
### ConfigMap `backup-cache`
185+
The `backup-cache` ConfigMap is an internal state tracker used during backup execution.
186+
* It stores the initial replica counts of all scaled-down workloads before the backup starts.
187+
* If the master script crashes, gets killed, or receives a termination signal (`SIGINT`/`SIGTERM`), it reads this cache during the recovery phase to reliably restore production workloads to their original state.
188+
* Once the backup and restoration sequence finishes successfully, the cache is automatically purged.
189+
190+
191+
### Secret `config`
192+
The `config` Secret is the core configuration secret required to operate. It must be created prior to running backup operations and contains remote storage credentials and target metadata:
193+
* `backup-map-namespace`: *(Optional)* The target namespace where `clustersnap-backup-map` and `clustersnap-backup-cache` are stored.
194+
* `cluster-name`: Name descriptor of the targeted Kubernetes cluster.
195+
* `storage-address`: Endpoint URL of the remote storage (e.g., `https://<account>.blob.core.windows.net/`).
196+
* `storage-username`: Storage account username or Access Key.
197+
* `storage-password`: Storage account password or Secret Access Key.
178198
### Workflows
179199
#### Volumes
200+
* Run a Kubernetes job to dump content of PVC mount points
180201
##### Export
181-
tofill
202+
203+
```mermaid
204+
stateDiagram-v2
205+
direction LR
206+
[*] --> Assessment: 1. Assessment
207+
Assessment --> WaitRelease: 2. Scale to 0 & Cache
208+
WaitRelease --> BackupJob: 3. Wait PVC Release
209+
BackupJob --> StreamUpload: 4. Trigger Backup Job
210+
StreamUpload --> Restoration: 5. Tar.gz Stream to Storage
211+
Restoration --> [*]: 6. Restore Replicas
212+
```
182213

183214
##### Import
184215
tofill
185216

186217

187218
#### PostgreSQL
188-
* Run a Kubernetes job based on the original PostgreSQL image (to get same version)
219+
* Run a Kubernetes job based on the original PostgreSQL image (to get same version) to dump databases
189220
##### Export
190-
* Run pg_dump
221+
222+
```mermaid
223+
stateDiagram-v2
224+
direction LR
225+
[*] --> Assessment: 1. Assessment
226+
Assessment --> SidecarJob: 2. Keep Replicas >0
227+
SidecarJob --> UnixPipe: 3. Trigger Dumper+Worker Job
228+
UnixPipe --> StreamUpload: 4. pg_dump to FIFO Pipe
229+
StreamUpload --> Cleanup: 5. Compress & Upload .sql.gz
230+
Cleanup --> [*]: 6. Purge Backup Jobs
231+
```
191232

192233
##### Import
193234
tofill

src/kubernetes/base.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@ class KubernetesBase:
99
def __init__(self, context: str = None, namespace: str = None):
1010
"""Init Kubernetes connexion, detect if CLI or in-cluster"""
1111
self.context = context
12-
if context:
13-
config.load_kube_config(context=context)
14-
else:
12+
self.namespace = namespace if namespace else "default"
13+
14+
try:
15+
config.load_incluster_config()
16+
except config.ConfigException:
1517
try:
16-
config.load_incluster_config()
18+
config.load_kube_config(context=context)
1719
except Exception as e:
18-
print(f"error: {e}")
19-
20-
self.namespace = namespace if namespace else "default"
20+
print(f"error: failed to load kubeconfig: {e}")
2121
self.core_v1 = client.CoreV1Api()
2222
self.apps_v1 = client.AppsV1Api()
2323
self.batch_v1 = client.BatchV1Api()
@@ -342,8 +342,7 @@ def _launch_generic_job(self, template_path: str, replacements: dict):
342342
if not os.path.exists(template_path):
343343
raise FileNotFoundError(f"Template path '{template_path}' not found.")
344344

345-
# Résolution dynamique et centralisée des secrets depuis l'environnement du master pod (default ou in-cluster)
346-
master_ns = self.get_current_namespace_from_pod() or "default"
345+
master_ns = replacements.get("master_ns") or self.get_current_namespace_from_pod() or "default"
347346
secret_data = self.get_secret_payload("clustersnap-config", master_ns)
348347

349348
with open(template_path, "r") as f:

0 commit comments

Comments
 (0)