1
+ {{- if .Values.backupJunoDataJob.enabled -}}
2
+ # Service Account for the Backup Job
3
+ apiVersion : v1
4
+ kind : ServiceAccount
5
+ metadata :
6
+ name : {{ .Values.deployment.namespace }}-backup-junodata-sa
7
+ namespace : {{ .Values.deployment.namespace }}
8
+ ---
9
+
10
+ # Role for Backup Job with necessary permissions
11
+ apiVersion : rbac.authorization.k8s.io/v1
12
+ kind : Role
13
+ metadata :
14
+ name : {{ .Values.deployment.namespace }}-backup-junodata-role
15
+ namespace : {{ .Values.deployment.namespace }}
16
+ rules :
17
+ - apiGroups : [ "", "apps","batch"]
18
+ resources : ["pods", "jobs", "persistentvolumeclaims"]
19
+ verbs : ["get", "list","create", "update", "patch", "delete"]
20
+ ---
21
+ # RoleBinding to bind Role with ServiceAccount
22
+ apiVersion : rbac.authorization.k8s.io/v1
23
+ kind : RoleBinding
24
+ metadata :
25
+ name : {{ .Values.deployment.namespace }}-backup-junodata-rolebinding
26
+ namespace : {{ .Values.deployment.namespace }}
27
+ subjects :
28
+ - kind : ServiceAccount
29
+ name : {{ .Values.deployment.namespace }}-backup-junodata-sa
30
+ namespace : {{ .Values.deployment.namespace }}
31
+ roleRef :
32
+ apiGroup : rbac.authorization.k8s.io
33
+ kind : Role
34
+ name : {{ .Values.deployment.namespace }}-backup-junodata-role
35
+ ---
36
+
37
+ # Secret to store R2 Cloud credentials
38
+ apiVersion : v1
39
+ kind : Secret
40
+ metadata :
41
+ name : {{ .Values.deployment.namespace }}-rclone-config
42
+ namespace : {{ .Values.deployment.namespace }}
43
+ stringData :
44
+ rclone.conf : |
45
+ [R2]
46
+ type = s3
47
+ provider = Cloudflare
48
+ env_auth = true
49
+ endpoint = https://d1cc7d59ae8f8dc2b1aa530c41b5c6ec.r2.cloudflarestorage.com
50
+ ---
51
+ # ConfigMap for cloning disk manifest
52
+ apiVersion : v1
53
+ kind : ConfigMap
54
+ metadata :
55
+ name : {{ .Values.deployment.namespace }}-cloning-disk-manifest
56
+ namespace : {{ .Values.deployment.namespace }}
57
+ data :
58
+ cloning-disk-manifest.yaml : |
59
+ apiVersion: v1
60
+ kind: PersistentVolumeClaim
61
+ metadata:
62
+ name: {{ .Values.deployment.namespace }}-pv-ssd-snapshot
63
+ namespace: {{ .Values.deployment.namespace }}
64
+ spec:
65
+ dataSource:
66
+ name: {{ .Values.backupJunoDataJob.dataSource }}
67
+ kind: PersistentVolumeClaim
68
+ accessModes:
69
+ - ReadWriteOnce
70
+ storageClassName: premium-rwo
71
+ resources:
72
+ requests:
73
+ storage: {{ .Values.backupJunoDataJob.storageSize }}
74
+ ---
75
+ apiVersion: v1
76
+ kind: PersistentVolumeClaim
77
+ metadata:
78
+ name: {{ .Values.deployment.namespace }}-juno-data-backup-pvc
79
+ namespace: {{ .Values.deployment.namespace }}
80
+ spec:
81
+ accessModes:
82
+ - ReadWriteOnce
83
+ storageClassName: premium-rwo
84
+ resources:
85
+ requests:
86
+ storage: {{ .Values.backupJunoDataJob.storageSize }}
87
+ ---
88
+ # ConfigMap for cloning juno manifest
89
+ apiVersion : v1
90
+ kind : ConfigMap
91
+ metadata :
92
+ name : {{ .Values.deployment.namespace }}-cloning-juno-manifest
93
+ namespace : {{ .Values.deployment.namespace }}
94
+ data :
95
+ cloning-juno-manifest.yaml : |
96
+ apiVersion: batch/v1
97
+ kind: Job
98
+ metadata:
99
+ name: {{ .Values.deployment.namespace }}-juno-data-archival-job
100
+ namespace: {{ .Values.deployment.namespace }}
101
+ spec:
102
+ ttlSecondsAfterFinished: 60
103
+ template:
104
+ spec:
105
+ serviceAccountName: {{ .Values.deployment.namespace }}-backup-junodata-sa
106
+ volumes:
107
+ - name: juno-data-volume
108
+ persistentVolumeClaim:
109
+ claimName: {{ .Values.deployment.namespace }}-pv-ssd-snapshot
110
+ - name: {{ .Values.deployment.namespace }}-rclone-config
111
+ secret:
112
+ secretName: {{ .Values.deployment.namespace }}-rclone-config
113
+ - name: tar-backup-volume
114
+ persistentVolumeClaim:
115
+ claimName: {{ .Values.deployment.namespace }}-juno-data-backup-pvc
116
+ initContainers:
117
+ - name: juno-archival-tar
118
+ image: busybox
119
+ command: ["/bin/sh", "-c"]
120
+ args:
121
+ - |
122
+ rm -rf /mnt/juno-tar-backup/*.tar &&
123
+ rm -rf /mnt/data/*.tar &&
124
+ tar -cf /mnt/juno-tar-backup/juno_{{ .Values.backupJunoDataJob.network }}_{{ .Values.deployment.imagetag }}_$(date +\%Y\%m\%d).tar --exclude=./lost+found -C /mnt/data . && sleep 10
125
+ volumeMounts:
126
+ - name: juno-data-volume
127
+ mountPath: /mnt/data
128
+ - name: tar-backup-volume
129
+ mountPath: /mnt/juno-tar-backup
130
+ containers:
131
+ - name: rclone-upload-container
132
+ image: rclone/rclone:latest
133
+ env:
134
+ - name: AWS_ACCESS_KEY_ID
135
+ valueFrom:
136
+ secretKeyRef:
137
+ name: {{ .Values.secret.data.targetName }}
138
+ key: r2_access_key_id
139
+ - name: AWS_SECRET_ACCESS_KEY
140
+ valueFrom:
141
+ secretKeyRef:
142
+ name: {{ .Values.secret.data.targetName }}
143
+ key: r2_secret_access_key
144
+ command: ["/bin/sh", "-c"]
145
+ args:
146
+ - |
147
+ apk add --no-cache curl &&
148
+ apk add --no-cache jq &&
149
+ latestBlockNumber=$(curl --location '{{ .Values.backupJunoDataJob.junoFreeEndpoint }}/{{ .Values.backupJunoDataJob.network }}-juno' --header 'Content-Type: application/json' --data '{ "jsonrpc": "2.0","method": "starknet_blockNumber", "id": 1}' | jq '.result') &&
150
+ echo "latestBlockNumber is $latestBlockNumber" &&
151
+ mv /mnt/juno-tar-backup/juno_{{ .Values.backupJunoDataJob.network }}_{{ .Values.deployment.imagetag }}*.tar /mnt/juno-tar-backup/juno_{{ .Values.backupJunoDataJob.network }}_{{ .Values.deployment.imagetag }}_$latestBlockNumber.tar &&
152
+ echo "/mnt/juno-tar-backup/juno_{{ .Values.backupJunoDataJob.network }}_{{ .Values.deployment.imagetag }}_$latestBlockNumber.tar" &&
153
+ rclone copy /mnt/juno-tar-backup/*.tar R2:/{{ .Values.backupJunoDataJob.bucketName }}/{{ .Values.backupJunoDataJob.network }}
154
+ volumeMounts:
155
+ - name: {{ .Values.deployment.namespace }}-rclone-config
156
+ mountPath: /config/rclone
157
+ - name: tar-backup-volume
158
+ mountPath: /mnt/juno-tar-backup
159
+ restartPolicy: OnFailure
160
+ ---
161
+ # CronJob for Backup Task
162
+ apiVersion : batch/v1
163
+ kind : CronJob
164
+ metadata :
165
+ name : {{ .Values.deployment.namespace }}-backup-junodata-cronjob
166
+ namespace : {{ .Values.deployment.namespace }}
167
+ spec :
168
+ schedule : " {{ .Values.backupJunoDataJob.backupSchedule }}"
169
+ concurrencyPolicy : Forbid
170
+ successfulJobsHistoryLimit : 1
171
+ failedJobsHistoryLimit : 1
172
+ jobTemplate :
173
+ spec :
174
+ completions : 1
175
+ ttlSecondsAfterFinished : 30
176
+ template :
177
+ spec :
178
+ serviceAccountName : {{ .Values.deployment.namespace }}-backup-junodata-sa
179
+ restartPolicy : Never
180
+ initContainers :
181
+ - name : copy-disk-kubectl-container
182
+ image : bitnami/kubectl:latest
183
+ command : ["/bin/sh"]
184
+ args : ["-c", "kubectl apply -f /cloning-disk-manifest/cloning-disk-manifest.yaml"]
185
+ volumeMounts :
186
+ - name : cloning-disk-manifest-volume
187
+ mountPath : /cloning-disk-manifest
188
+ containers :
189
+ - name : clone-juno-kubectl-container
190
+ image : bitnami/kubectl:latest
191
+ command : ["/bin/sh"]
192
+ args : ["-c", "kubectl apply -f /cloning-juno-manifest/cloning-juno-manifest.yaml"]
193
+ volumeMounts :
194
+ - name : cloning-juno-manifest-volume
195
+ mountPath : /cloning-juno-manifest
196
+ volumes :
197
+ - name : cloning-disk-manifest-volume
198
+ configMap :
199
+ name : {{ .Values.deployment.namespace }}-cloning-disk-manifest
200
+ - name : cloning-juno-manifest-volume
201
+ configMap :
202
+ name : {{ .Values.deployment.namespace }}-cloning-juno-manifest
203
+ ---
204
+ # CronJob for Cleaning up Completed Pods and PVCs
205
+ apiVersion : batch/v1
206
+ kind : CronJob
207
+ metadata :
208
+ name : {{ .Values.deployment.namespace }}-delete-used-pvc
209
+ namespace : {{ .Values.deployment.namespace }}
210
+ spec :
211
+ schedule : " {{ .Values.backupJunoDataJob.cleanupSchedule }}"
212
+ concurrencyPolicy : Forbid
213
+ successfulJobsHistoryLimit : 1
214
+ failedJobsHistoryLimit : 1
215
+ jobTemplate :
216
+ spec :
217
+ completions : 1
218
+ ttlSecondsAfterFinished : 30
219
+ template :
220
+ spec :
221
+ serviceAccountName : {{ .Values.deployment.namespace }}-backup-junodata-sa
222
+ restartPolicy : OnFailure
223
+ containers :
224
+ - name : kubectl-container
225
+ image : bitnami/kubectl:latest
226
+ command :
227
+ - " /bin/bash"
228
+ - " -c"
229
+ - |
230
+ # Delete PVC if not used
231
+ describe_output=$(kubectl describe pvc {{ .Values.deployment.namespace }}-pv-ssd-snapshot)
232
+ if echo "$describe_output" | grep -q "Used By:[[:space:]]*<none>"; then
233
+ echo "Deleting {{ .Values.deployment.namespace }}-pv-ssd-snapshot..."
234
+ kubectl delete pvc {{ .Values.deployment.namespace }}-pv-ssd-snapshot
235
+ sleep 30
236
+ fi
237
+ describe_output=$(kubectl describe pvc {{ .Values.deployment.namespace }}-juno-data-backup-pvc)
238
+ if echo "$describe_output" | grep -q "Used By:[[:space:]]*<none>"; then
239
+ echo "Deleting {{ .Values.deployment.namespace }}-juno-data-backup-pvc..."
240
+ kubectl delete pvc {{ .Values.deployment.namespace }}-juno-data-backup-pvc
241
+ sleep 30
242
+ fi
243
+ ---
244
+ # CronJob for Cleaning up Completed Pods and PVCs
245
+ apiVersion : batch/v1
246
+ kind : CronJob
247
+ metadata :
248
+ name : {{ .Values.deployment.namespace }}-r2-retention-cronjob
249
+ namespace : {{ .Values.deployment.namespace }}
250
+ spec :
251
+ schedule : " 0 0 * * */2"
252
+ jobTemplate :
253
+ spec :
254
+ completions : 1
255
+ ttlSecondsAfterFinished : 30
256
+ template :
257
+ spec :
258
+ restartPolicy : OnFailure
259
+ containers :
260
+ - name : {{ .Values.deployment.namespace }}-r2-retention
261
+ image : ubuntu:latest
262
+ env :
263
+ - name : RETENTION_LIMIT
264
+ value : " {{ .Values.backupJunoDataJob.retentionLimit }}"
265
+ - name : API_TOKEN
266
+ valueFrom :
267
+ secretKeyRef :
268
+ name : {{ .Values.secret.data.targetName }}
269
+ key : r2_api_token
270
+ - name : ACCOUNT_ID
271
+ value : " d1cc7d59ae8f8dc2b1aa530c41b5c6ec"
272
+ - name : BUCKET_NAME
273
+ value : " {{ .Values.backupJunoDataJob.bucketName }}"
274
+ command :
275
+ - /bin/sh
276
+ - -c
277
+ - |
278
+ #!/bin/sh
279
+ mkdir -p /var/lib/apt/lists/partial
280
+ apt-get update && apt-get install -y curl jq
281
+ # Constants
282
+ API_TOKEN="$API_TOKEN"
283
+ RETENTION_LIMIT="$RETENTION_LIMIT"
284
+ ACCOUNT_ID="$ACCOUNT_ID"
285
+ BUCKET_NAME="$BUCKET_NAME"
286
+
287
+ # Construct the Cloudflare API URL with account ID and bucket name
288
+ CLOUDFLARE_API_URL="https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/r2/buckets/$BUCKET_NAME/objects?prefix={{ .Values.backupJunoDataJob.network }}/"
289
+ # Get the list of objects with the specified prefix
290
+ objects=$(curl -s -X GET "$CLOUDFLARE_API_URL" -H "Authorization: Bearer $API_TOKEN" | jq -r '.result')
291
+
292
+ # Check if the number of objects exceeds the retention limit
293
+ object_count=$(echo "$objects" | jq length)
294
+ echo "total backup number is $object_count"
295
+
296
+ if [ "$object_count" -le "$RETENTION_LIMIT" ]; then
297
+ echo "exiting...."
298
+ exit 0
299
+ fi
300
+ delete_number=$((object_count - RETENTION_LIMIT))
301
+ # Sort the objects by last_modified date and delete the oldest ones
302
+ echo "$objects" | jq -r '.[] | [.key, .last_modified] | @tsv' | sort -k2 | head -n "$delete_number" | while IFS=$'\t' read -r key last_modified; do
303
+ delete_url="https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/r2/buckets/$BUCKET_NAME/objects/${key}tar"
304
+ echo "Deleting ${key}tar at $delete_url"
305
+ delete_response=$(curl -s -X DELETE "$delete_url" -H "Authorization: Bearer $API_TOKEN")
306
+ echo "Delete response: $delete_response"
307
+ done
308
+ ---
309
+ {{- end -}}
0 commit comments