From a76d4d8a13e5bbc3549191e67c6c2a0245ad13ef Mon Sep 17 00:00:00 2001 From: Bum-Seok Hwang Date: Wed, 19 Jan 2022 04:48:00 +0000 Subject: [PATCH 1/2] =?UTF-8?q?fix:=20CronJob=EC=97=90=EC=84=9C=20patch?= =?UTF-8?q?=EA=B0=80=20=EB=AF=B8=EB=8F=99=EC=9E=91=20=ED=95=98=EB=8A=94=20?= =?UTF-8?q?=EB=AC=B8=EC=A0=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- controller/onUpdateImageHash.go | 28 +++------------- util/kubernetes.go | 57 ++++++++++++++++++++++++++++++--- 2 files changed, 57 insertions(+), 28 deletions(-) diff --git a/controller/onUpdateImageHash.go b/controller/onUpdateImageHash.go index 78f3387..bc5efe9 100644 --- a/controller/onUpdateImageHash.go +++ b/controller/onUpdateImageHash.go @@ -1,7 +1,6 @@ package controller import ( - "encoding/json" "fmt" "github.com/pubg/kube-image-deployer/util" @@ -22,22 +21,6 @@ type imageUpdateNotify struct { imageString string } -type Container struct { - Name string `json:"name"` - Image string `json:"image"` -} - -type ImageStrategicPatch struct { - Spec struct { - Template struct { - Spec struct { - Containers []Container `json:"containers,omitempty"` - InitContainers []Container `json:"initContainers,omitempty"` - } `json:"spec"` - } `json:"template"` - } `json:"spec"` -} - func (c *Controller) getPatchMapByUpdates(updates []imageUpdateNotify) map[string][]patch { // key 별로 kubernetes patch apply를 위한 리스트를 생성 patchMap := make(map[string][]patch) @@ -74,9 +57,8 @@ func (c *Controller) getPatchMapByUpdates(updates []imageUpdateNotify) map[strin func (c *Controller) applyPatchList(key string, patchList []patch) error { - imageStrategicPatch := ImageStrategicPatch{} - Containers := make([]Container, 0) - InitContainers := make([]Container, 0) + Containers := make([]util.Container, 0) + InitContainers := make([]util.Container, 0) namespace, name := util.GetNamespaceNameByKey(key) if namespace == "" || name == "" { @@ -109,7 +91,7 @@ func (c *Controller) applyPatchList(key string, patchList []patch) error { // 이미지 변경 체크 if currentContainer.Image != patch.imageString { - container := Container{ + container := util.Container{ Name: patch.containerName, Image: patch.imageString, } @@ -127,9 +109,7 @@ func (c *Controller) applyPatchList(key string, patchList []patch) error { return nil } - imageStrategicPatch.Spec.Template.Spec.Containers = Containers - imageStrategicPatch.Spec.Template.Spec.InitContainers = InitContainers - patchString, err := json.Marshal(imageStrategicPatch) + patchString, err := util.GetImageStrategicPatchJson(obj, Containers, InitContainers) if err != nil { return fmt.Errorf("[%s] OnUpdateImageString patch marshal error %+v, err=%s", c.resource, patchList, err) diff --git a/util/kubernetes.go b/util/kubernetes.go index 73b8486..017599c 100644 --- a/util/kubernetes.go +++ b/util/kubernetes.go @@ -1,14 +1,46 @@ package util import ( + "encoding/json" "fmt" "strings" appV1 "k8s.io/api/apps/v1" - batchV1 "k8s.io/api/batch/v1" + batchV1Beta1 "k8s.io/api/batch/v1beta1" coreV1 "k8s.io/api/core/v1" ) +type Container struct { + Name string `json:"name"` + Image string `json:"image"` +} + +type ImageStrategicPatch struct { + Spec struct { + Template struct { + Spec struct { + Containers []Container `json:"containers,omitempty"` + InitContainers []Container `json:"initContainers,omitempty"` + } `json:"spec"` + } `json:"template"` + } `json:"spec"` +} + +type ImageStrategicPatchCronJob struct { + Spec struct { + JobTemplate struct { + Spec struct { + Template struct { + Spec struct { + Containers []Container `json:"containers,omitempty"` + InitContainers []Container `json:"initContainers,omitempty"` + } `json:"spec"` + } `json:"template"` + } `json:"spec"` + } `json:"jobTemplate"` + } `json:"spec"` +} + func GetAnnotations(obj interface{}) (map[string]string, error) { switch t := obj.(type) { case *appV1.Deployment: @@ -17,7 +49,7 @@ func GetAnnotations(obj interface{}) (map[string]string, error) { return t.Annotations, nil case *appV1.DaemonSet: return t.Annotations, nil - case *batchV1.CronJob: + case *batchV1Beta1.CronJob: return t.Annotations, nil default: return make(map[string]string), fmt.Errorf("GetAnnotations unknown type %T", t) @@ -32,7 +64,7 @@ func GetContainers(obj interface{}) ([]coreV1.Container, error) { return t.Spec.Template.Spec.Containers, nil case *appV1.DaemonSet: return t.Spec.Template.Spec.Containers, nil - case *batchV1.CronJob: + case *batchV1Beta1.CronJob: return t.Spec.JobTemplate.Spec.Template.Spec.Containers, nil default: return make([]coreV1.Container, 0), fmt.Errorf("GetContainers unknown type %T", t) @@ -47,7 +79,7 @@ func GetInitContainers(obj interface{}) ([]coreV1.Container, error) { return t.Spec.Template.Spec.InitContainers, nil case *appV1.DaemonSet: return t.Spec.Template.Spec.InitContainers, nil - case *batchV1.CronJob: + case *batchV1Beta1.CronJob: return t.Spec.JobTemplate.Spec.Template.Spec.InitContainers, nil default: return make([]coreV1.Container, 0), fmt.Errorf("GetInitContainers unknown type %T", t) @@ -91,3 +123,20 @@ func GetNamespaceNameByKey(key string) (namespace string, name string) { } return } + +func GetImageStrategicPatchJson(obj interface{}, containers, initContainers []Container) ([]byte, error) { + switch obj.(type) { + case *batchV1Beta1.CronJob: + imageStrategicPatch := ImageStrategicPatchCronJob{} + imageStrategicPatch.Spec.JobTemplate.Spec.Template.Spec.Containers = containers + imageStrategicPatch.Spec.JobTemplate.Spec.Template.Spec.InitContainers = initContainers + patchJson, err := json.Marshal(imageStrategicPatch) + return patchJson, err + default: + imageStrategicPatch := ImageStrategicPatch{} + imageStrategicPatch.Spec.Template.Spec.Containers = containers + imageStrategicPatch.Spec.Template.Spec.InitContainers = initContainers + patchJson, err := json.Marshal(imageStrategicPatch) + return patchJson, err + } +} From f90eeca3f125891c1b6fe53ae4749482e82c54ee Mon Sep 17 00:00:00 2001 From: Bum-Seok Hwang Date: Wed, 19 Jan 2022 07:00:45 +0000 Subject: [PATCH 2/2] =?UTF-8?q?add:=20cronjob=20v1=20=EC=A7=80=EC=9B=90?= =?UTF-8?q?=EC=98=B5=EC=85=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.go | 22 ++++++++++++++++++---- readme.md | 2 ++ util/kubernetes.go | 19 +++++++++++++++---- 3 files changed, 35 insertions(+), 8 deletions(-) diff --git a/main.go b/main.go index aea4dd7..921bf98 100644 --- a/main.go +++ b/main.go @@ -15,6 +15,7 @@ import ( "github.com/pubg/kube-image-deployer/remoteRegistry/docker" "github.com/pubg/kube-image-deployer/watcher" appV1 "k8s.io/api/apps/v1" + batchV1 "k8s.io/api/batch/v1" batchV1beta1 "k8s.io/api/batch/v1beta1" metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" @@ -31,6 +32,7 @@ var ( offStatefulsets = *flag.Bool("off-statefulsets", false, "disable statefulsets") offDaemonsets = *flag.Bool("off-daemonsets", false, "disable daemonsets") offCronjobs = *flag.Bool("off-cronjobs", false, "disable cronjobs") + useCronJobV1 = *flag.Bool("use-cronjob-v1", false, "use cronjob version v1 instead of v1beta1") imageStringCacheTTLSec = *flag.Uint("image-hash-cache-ttl-sec", 60, "image hash cache TTL in seconds") imageCheckIntervalSec = *flag.Uint("image-check-interval-sec", 10, "image check interval in seconds") controllerWatchKey = *flag.String("controller-watch-key", "kube-image-deployer", "controller watch key") @@ -68,6 +70,9 @@ func init() { if os.Getenv("OFF_CRONJOBS") != "" { offCronjobs = true } + if os.Getenv("USE_CRONJOB_V1") != "" { + useCronJobV1 = true + } if os.Getenv("IMAGE_HASH_CACHE_TTL_SEC") != "" { if v, err := strconv.ParseUint(os.Getenv("IMAGE_HASH_CACHE_TTL_SEC"), 10, 32); err == nil { imageStringCacheTTLSec = uint(v) @@ -100,6 +105,7 @@ func init() { "offStatefulsets": offStatefulsets, "offDaemonsets": offDaemonsets, "offCronjobs": offCronjobs, + "useCronJobV1": useCronJobV1, "imageStringCacheTTLSec": imageStringCacheTTLSec, "imageCheckIntervalSec": imageCheckIntervalSec, "controllerWatchKey": controllerWatchKey, @@ -181,11 +187,19 @@ func runWatchers(stopCh chan struct{}) { } if !offCronjobs { // cronjobs watcher - applyStrategicMergePatch := func(namespace string, name string, data []byte) error { - _, err := clientset.BatchV1beta1().CronJobs(namespace).Patch(context.TODO(), name, types.StrategicMergePatchType, data, metaV1.PatchOptions{}) - return err + if useCronJobV1 { + applyStrategicMergePatch := func(namespace string, name string, data []byte) error { + _, err := clientset.BatchV1().CronJobs(namespace).Patch(context.TODO(), name, types.StrategicMergePatchType, data, metaV1.PatchOptions{}) + return err + } + go watcher.NewWatcher("cronjobs", stopCh, logger, cache.NewFilteredListWatchFromClient(clientset.BatchV1().RESTClient(), "cronjobs", controllerWatchNamespace, optionsModifier), &batchV1.CronJob{}, imageNotifier, controllerWatchKey, applyStrategicMergePatch) + } else { + applyStrategicMergePatch := func(namespace string, name string, data []byte) error { + _, err := clientset.BatchV1beta1().CronJobs(namespace).Patch(context.TODO(), name, types.StrategicMergePatchType, data, metaV1.PatchOptions{}) + return err + } + go watcher.NewWatcher("cronjobs", stopCh, logger, cache.NewFilteredListWatchFromClient(clientset.BatchV1beta1().RESTClient(), "cronjobs", controllerWatchNamespace, optionsModifier), &batchV1beta1.CronJob{}, imageNotifier, controllerWatchKey, applyStrategicMergePatch) } - go watcher.NewWatcher("cronjobs", stopCh, logger, cache.NewFilteredListWatchFromClient(clientset.BatchV1beta1().RESTClient(), "cronjobs", controllerWatchNamespace, optionsModifier), &batchV1beta1.CronJob{}, imageNotifier, controllerWatchKey, applyStrategicMergePatch) } } diff --git a/readme.md b/readme.md index 04698ef..ea55e71 100644 --- a/readme.md +++ b/readme.md @@ -19,6 +19,7 @@ offDeployments = *flag.Bool("off-deployments", false, "disable deploym offStatefulsets = *flag.Bool("off-statefulsets", false, "disable statefulsets") offDaemonsets = *flag.Bool("off-daemonsets", false, "disable daemonsets") offCronjobs = *flag.Bool("off-cronjobs", false, "disable cronjobs") +useCronJobV1 = *flag.Bool("use-cronjob-v1", false, "use cronjob version v1 instead of v1beta1") imageStringCacheTTLSec = *flag.Uint("image-hash-cache-ttl-sec", 60, "image hash cache TTL in seconds") imageCheckIntervalSec = *flag.Uint("image-check-interval-sec", 10, "image check interval in seconds") controllerWatchKey = *flag.String("controller-watch-key", "kube-image-deployer", "controller watch key") @@ -35,6 +36,7 @@ OFF_DEPLOYMENTS= OFF_STATEFULSETS= OFF_DAEMONSETS= OFF_CRONJOBS= +USE_CRONJOB_V1= IMAGE_HASH_CACHE_TTL_SEC= IMAGE_CHECK_INTERVAL_SEC= CONTROLLER_WATCH_KEY= diff --git a/util/kubernetes.go b/util/kubernetes.go index 017599c..42352c4 100644 --- a/util/kubernetes.go +++ b/util/kubernetes.go @@ -6,6 +6,7 @@ import ( "strings" appV1 "k8s.io/api/apps/v1" + batchV1 "k8s.io/api/batch/v1" batchV1Beta1 "k8s.io/api/batch/v1beta1" coreV1 "k8s.io/api/core/v1" ) @@ -51,6 +52,8 @@ func GetAnnotations(obj interface{}) (map[string]string, error) { return t.Annotations, nil case *batchV1Beta1.CronJob: return t.Annotations, nil + case *batchV1.CronJob: + return t.Annotations, nil default: return make(map[string]string), fmt.Errorf("GetAnnotations unknown type %T", t) } @@ -66,6 +69,8 @@ func GetContainers(obj interface{}) ([]coreV1.Container, error) { return t.Spec.Template.Spec.Containers, nil case *batchV1Beta1.CronJob: return t.Spec.JobTemplate.Spec.Template.Spec.Containers, nil + case *batchV1.CronJob: + return t.Spec.JobTemplate.Spec.Template.Spec.Containers, nil default: return make([]coreV1.Container, 0), fmt.Errorf("GetContainers unknown type %T", t) } @@ -81,6 +86,8 @@ func GetInitContainers(obj interface{}) ([]coreV1.Container, error) { return t.Spec.Template.Spec.InitContainers, nil case *batchV1Beta1.CronJob: return t.Spec.JobTemplate.Spec.Template.Spec.InitContainers, nil + case *batchV1.CronJob: + return t.Spec.JobTemplate.Spec.Template.Spec.InitContainers, nil default: return make([]coreV1.Container, 0), fmt.Errorf("GetInitContainers unknown type %T", t) } @@ -125,18 +132,22 @@ func GetNamespaceNameByKey(key string) (namespace string, name string) { } func GetImageStrategicPatchJson(obj interface{}, containers, initContainers []Container) ([]byte, error) { + var imageStrategicPatch interface{} + switch obj.(type) { case *batchV1Beta1.CronJob: imageStrategicPatch := ImageStrategicPatchCronJob{} imageStrategicPatch.Spec.JobTemplate.Spec.Template.Spec.Containers = containers imageStrategicPatch.Spec.JobTemplate.Spec.Template.Spec.InitContainers = initContainers - patchJson, err := json.Marshal(imageStrategicPatch) - return patchJson, err + case *batchV1.CronJob: + imageStrategicPatch := ImageStrategicPatchCronJob{} + imageStrategicPatch.Spec.JobTemplate.Spec.Template.Spec.Containers = containers + imageStrategicPatch.Spec.JobTemplate.Spec.Template.Spec.InitContainers = initContainers default: imageStrategicPatch := ImageStrategicPatch{} imageStrategicPatch.Spec.Template.Spec.Containers = containers imageStrategicPatch.Spec.Template.Spec.InitContainers = initContainers - patchJson, err := json.Marshal(imageStrategicPatch) - return patchJson, err } + patchJson, err := json.Marshal(imageStrategicPatch) + return patchJson, err }