Skip to content

Commit 574129d

Browse files
authored
support recreating k8s jobs when configmap/secret changed (#808)
* support recreating k8s jobs when configmap/secret changed Co-authored-by: Mohamed Sekour <mohamed.sekour@exfo.com> * add unit tests * fix tests
1 parent 919d0cc commit 574129d

11 files changed

Lines changed: 627 additions & 12 deletions

File tree

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,13 @@ spec:
5050
metadata:
5151
```
5252
53-
This will discover deploymentconfigs/deployments/daemonsets/statefulset/rollouts automatically where `foo-configmap` or `foo-secret` is being used either via environment variable or from volume mount. And it will perform rolling upgrade on related pods when `foo-configmap` or `foo-secret`are updated.
53+
This will discover deploymentconfigs/deployments/daemonsets/statefulset/rollouts/cronjobs/jobs automatically where `foo-configmap` or `foo-secret` is being used either via environment variable or from volume mount. And it will perform rolling upgrade on related pods when `foo-configmap` or `foo-secret`are updated.
5454

5555
You can filter it by the type of monitored resource and use typed versions of `auto` annotation. If you want to discover changes only in mounted `Secret`s and ignore changes in `ConfigMap`s, add `secret.reloader.stakater.com/auto` annotation instead. Analogously, you can use `configmap.reloader.stakater.com/auto` annotation to look for changes in mounted `ConfigMap`, changes in any of mounted `Secret`s will not trigger a rolling upgrade on related pods.
5656

5757
You can also restrict this discovery to only `ConfigMap` or `Secret` objects that
5858
are tagged with a special annotation. To take advantage of that, annotate
59-
your deploymentconfigs/deployments/daemonsets/statefulset/rollouts like this:
59+
your deploymentconfigs/deployments/daemonsets/statefulset/rollouts/cronjobs/jobs like this:
6060

6161
```yaml
6262
kind: Deployment
@@ -91,7 +91,7 @@ not.
9191

9292
Similarly, `reloader.stakater.com/auto` and its typed version (`secret.reloader.stakater.com/auto` or `configmap.reloader.stakater.com/auto`) do not work together. If you have both annotations in your deployment, then only one of them needs to be true to trigger the restart. For example, having both `reloader.stakater.com/auto: "true"` and `secret.reloader.stakater.com/auto: "false"` or both `reloader.stakater.com/auto: "false"` and `secret.reloader.stakater.com/auto: "true"` will restart upon a change in a secret it uses.
9393

94-
We can also specify a specific configmap or secret which would trigger rolling upgrade only upon change in our specified configmap or secret, this way, it will not trigger rolling upgrade upon changes in all configmaps or secrets used in a `deploymentconfig`, `deployment`, `daemonset`, `statefulset` or `rollout`.
94+
We can also specify a specific configmap or secret which would trigger rolling upgrade only upon change in our specified configmap or secret, this way, it will not trigger rolling upgrade upon changes in all configmaps or secrets used in a `deploymentconfig`, `deployment`, `daemonset`, `statefulset`, `rollout`, `cronJob` or `job`.
9595
To do this either set the auto annotation to `"false"` (`reloader.stakater.com/auto: "false"`) or remove it altogether, and use annotations for [Configmap](.#Configmap) or [Secret](.#Secret).
9696
9797
It's also possible to enable auto reloading for all resources, by setting the `--auto-reload-all` flag.
@@ -158,7 +158,7 @@ spec:
158158

159159
- Reloader also supports [sealed-secrets](https://github.com/bitnami-labs/sealed-secrets). [Here](docs/Reloader-with-Sealed-Secrets.md) are the steps to use sealed-secrets with Reloader.
160160
- For [`rollouts`](https://github.com/argoproj/argo-rollouts/) Reloader simply triggers a change is up to you how you configure the `rollout` strategy.
161-
- `reloader.stakater.com/auto: "true"` will only reload the pod, if the configmap or secret is used (as a volume mount or as an env) in `DeploymentConfigs/Deployment/Daemonsets/Statefulsets`
161+
- `reloader.stakater.com/auto: "true"` will only reload the pod, if the configmap or secret is used (as a volume mount or as an env) in `DeploymentConfigs/Deployment/Daemonsets/Statefulsets/CronJobs/Jobs`
162162
- `secret.reloader.stakater.com/reload` or `configmap.reloader.stakater.com/reload` annotation will reload the pod upon changes in specified configmap or secret, irrespective of the usage of configmap or secret.
163163
- you may override the auto annotation with the `--auto-annotation` flag
164164
- you may override the secret typed auto annotation with the `--secret-auto-annotation` flag

deployments/kubernetes/chart/reloader/templates/clusterrole.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,9 @@ rules:
8989
- jobs
9090
verbs:
9191
- create
92+
- delete
93+
- list
94+
- get
9295
{{- if .Values.reloader.enableHA }}
9396
- apiGroups:
9497
- "coordination.k8s.io"

deployments/kubernetes/chart/reloader/templates/role.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ rules:
8080
- jobs
8181
verbs:
8282
- create
83+
- delete
84+
- list
85+
- get
8386
{{- if .Values.reloader.enableHA }}
8487
- apiGroups:
8588
- "coordination.k8s.io"

deployments/kubernetes/manifests/clusterrole.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ rules:
4848
- jobs
4949
verbs:
5050
- create
51+
- delete
52+
- list
53+
- get
5154
- apiGroups:
5255
- ""
5356
resources:

deployments/kubernetes/reloader.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ rules:
5252
- jobs
5353
verbs:
5454
- create
55+
- delete
56+
- list
57+
- get
5558
- apiGroups:
5659
- ""
5760
resources:

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ require (
1010
github.com/prometheus/client_golang v1.20.5
1111
github.com/sirupsen/logrus v1.9.3
1212
github.com/spf13/cobra v1.8.1
13+
github.com/stretchr/testify v1.9.0
1314
k8s.io/api v0.31.1
1415
k8s.io/apimachinery v0.31.1
1516
k8s.io/client-go v0.31.1
@@ -47,6 +48,7 @@ require (
4748
github.com/moul/http2curl v1.0.0 // indirect
4849
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
4950
github.com/pkg/errors v0.9.1 // indirect
51+
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
5052
github.com/prometheus/client_model v0.6.1 // indirect
5153
github.com/prometheus/common v0.55.0 // indirect
5254
github.com/prometheus/procfs v0.15.1 // indirect

go.sum

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,8 +178,6 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
178178
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
179179
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
180180
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
181-
github.com/prometheus/client_golang v1.20.4 h1:Tgh3Yr67PaOv/uTqloMsCEdeuFTatm5zIq5+qNN23vI=
182-
github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
183181
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
184182
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
185183
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=

internal/pkg/callbacks/rolling_upgrade.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,26 @@ func GetCronJobItems(clients kube.Clients, namespace string) []runtime.Object {
9292
return items
9393
}
9494

95+
// GetJobItems returns the jobs in given namespace
96+
func GetJobItems(clients kube.Clients, namespace string) []runtime.Object {
97+
jobs, err := clients.KubernetesClient.BatchV1().Jobs(namespace).List(context.TODO(), meta_v1.ListOptions{})
98+
if err != nil {
99+
logrus.Errorf("Failed to list jobs %v", err)
100+
}
101+
102+
items := make([]runtime.Object, len(jobs.Items))
103+
// Ensure we always have pod annotations to add to
104+
for i, v := range jobs.Items {
105+
if v.Spec.Template.ObjectMeta.Annotations == nil {
106+
annotations := make(map[string]string)
107+
jobs.Items[i].Spec.Template.ObjectMeta.Annotations = annotations
108+
}
109+
items[i] = &jobs.Items[i]
110+
}
111+
112+
return items
113+
}
114+
95115
// GetDaemonSetItems returns the daemonSets in given namespace
96116
func GetDaemonSetItems(clients kube.Clients, namespace string) []runtime.Object {
97117
daemonSets, err := clients.KubernetesClient.AppsV1().DaemonSets(namespace).List(context.TODO(), meta_v1.ListOptions{})
@@ -178,6 +198,11 @@ func GetCronJobAnnotations(item runtime.Object) map[string]string {
178198
return item.(*batchv1.CronJob).ObjectMeta.Annotations
179199
}
180200

201+
// GetJobAnnotations returns the annotations of given job
202+
func GetJobAnnotations(item runtime.Object) map[string]string {
203+
return item.(*batchv1.Job).ObjectMeta.Annotations
204+
}
205+
181206
// GetDaemonSetAnnotations returns the annotations of given daemonSet
182207
func GetDaemonSetAnnotations(item runtime.Object) map[string]string {
183208
return item.(*appsv1.DaemonSet).ObjectMeta.Annotations
@@ -208,6 +233,11 @@ func GetCronJobPodAnnotations(item runtime.Object) map[string]string {
208233
return item.(*batchv1.CronJob).Spec.JobTemplate.Spec.Template.ObjectMeta.Annotations
209234
}
210235

236+
// GetJobPodAnnotations returns the pod's annotations of given job
237+
func GetJobPodAnnotations(item runtime.Object) map[string]string {
238+
return item.(*batchv1.Job).Spec.Template.ObjectMeta.Annotations
239+
}
240+
211241
// GetDaemonSetPodAnnotations returns the pod's annotations of given daemonSet
212242
func GetDaemonSetPodAnnotations(item runtime.Object) map[string]string {
213243
return item.(*appsv1.DaemonSet).Spec.Template.ObjectMeta.Annotations
@@ -238,6 +268,11 @@ func GetCronJobContainers(item runtime.Object) []v1.Container {
238268
return item.(*batchv1.CronJob).Spec.JobTemplate.Spec.Template.Spec.Containers
239269
}
240270

271+
// GetJobContainers returns the containers of given job
272+
func GetJobContainers(item runtime.Object) []v1.Container {
273+
return item.(*batchv1.Job).Spec.Template.Spec.Containers
274+
}
275+
241276
// GetDaemonSetContainers returns the containers of given daemonSet
242277
func GetDaemonSetContainers(item runtime.Object) []v1.Container {
243278
return item.(*appsv1.DaemonSet).Spec.Template.Spec.Containers
@@ -268,6 +303,11 @@ func GetCronJobInitContainers(item runtime.Object) []v1.Container {
268303
return item.(*batchv1.CronJob).Spec.JobTemplate.Spec.Template.Spec.InitContainers
269304
}
270305

306+
// GetJobInitContainers returns the containers of given job
307+
func GetJobInitContainers(item runtime.Object) []v1.Container {
308+
return item.(*batchv1.Job).Spec.Template.Spec.InitContainers
309+
}
310+
271311
// GetDaemonSetInitContainers returns the containers of given daemonSet
272312
func GetDaemonSetInitContainers(item runtime.Object) []v1.Container {
273313
return item.(*appsv1.DaemonSet).Spec.Template.Spec.InitContainers
@@ -307,6 +347,38 @@ func CreateJobFromCronjob(clients kube.Clients, namespace string, resource runti
307347
return err
308348
}
309349

350+
// ReCreateJobFromjob performs rolling upgrade on job
351+
func ReCreateJobFromjob(clients kube.Clients, namespace string, resource runtime.Object) error {
352+
oldJob := resource.(*batchv1.Job)
353+
job := oldJob.DeepCopy()
354+
355+
// Delete the old job
356+
policy := meta_v1.DeletePropagationBackground
357+
err := clients.KubernetesClient.BatchV1().Jobs(namespace).Delete(context.TODO(), job.Name, meta_v1.DeleteOptions{PropagationPolicy: &policy})
358+
if err != nil {
359+
return err
360+
}
361+
362+
// Remove fields that should not be specified when creating a new Job
363+
job.ObjectMeta.ResourceVersion = ""
364+
job.ObjectMeta.UID = ""
365+
job.ObjectMeta.CreationTimestamp = meta_v1.Time{}
366+
job.Status = batchv1.JobStatus{}
367+
368+
// Remove problematic labels
369+
delete(job.Spec.Template.Labels, "controller-uid")
370+
delete(job.Spec.Template.Labels, batchv1.ControllerUidLabel)
371+
delete(job.Spec.Template.Labels, batchv1.JobNameLabel)
372+
delete(job.Spec.Template.Labels, "job-name")
373+
374+
// Remove the selector to allow it to be auto-generated
375+
job.Spec.Selector = nil
376+
377+
// Create the new job with same spec
378+
_, err = clients.KubernetesClient.BatchV1().Jobs(namespace).Create(context.TODO(), job, meta_v1.CreateOptions{FieldManager: "Reloader"})
379+
return err
380+
}
381+
310382
// UpdateDaemonSet performs rolling upgrade on daemonSet
311383
func UpdateDaemonSet(clients kube.Clients, namespace string, resource runtime.Object) error {
312384
daemonSet := resource.(*appsv1.DaemonSet)
@@ -352,6 +424,11 @@ func GetCronJobVolumes(item runtime.Object) []v1.Volume {
352424
return item.(*batchv1.CronJob).Spec.JobTemplate.Spec.Template.Spec.Volumes
353425
}
354426

427+
// GetJobVolumes returns the Volumes of given job
428+
func GetJobVolumes(item runtime.Object) []v1.Volume {
429+
return item.(*batchv1.Job).Spec.Template.Spec.Volumes
430+
}
431+
355432
// GetDaemonSetVolumes returns the Volumes of given daemonSet
356433
func GetDaemonSetVolumes(item runtime.Object) []v1.Volume {
357434
return item.(*appsv1.DaemonSet).Spec.Template.Spec.Volumes

0 commit comments

Comments
 (0)