From 5fd1e16f1ac6a06ceb1429f05e0f6ceedfb4aefe Mon Sep 17 00:00:00 2001 From: Patrice Ferlet Date: Tue, 24 May 2022 14:44:31 +0200 Subject: [PATCH 01/26] WIP, possibility to create cronjob --- generator/crontabs.go | 84 ++++++++++++++++++++++++++++++++++++++++++ generator/main.go | 9 ++++- helm/cronTab.go | 60 ++++++++++++++++++++++++++++++ helm/deployment.go | 1 + helm/labels.go | 3 ++ helm/role.go | 33 +++++++++++++++++ helm/roleBinding.go | 44 ++++++++++++++++++++++ helm/serviceAccount.go | 18 +++++++++ 8 files changed, 251 insertions(+), 1 deletion(-) create mode 100644 generator/crontabs.go create mode 100644 helm/cronTab.go create mode 100644 helm/role.go create mode 100644 helm/roleBinding.go create mode 100644 helm/serviceAccount.go diff --git a/generator/crontabs.go b/generator/crontabs.go new file mode 100644 index 0000000..adf7006 --- /dev/null +++ b/generator/crontabs.go @@ -0,0 +1,84 @@ +package generator + +import ( + "fmt" + "katenary/helm" + "log" + + "github.com/compose-spec/compose-go/types" + "gopkg.in/yaml.v3" +) + +const ( + cronMulti = `pods=$(kubectl get pods --selector=%s/component=%s,%s/resource=deployment -o jsonpath='{.items[*].metadata.name}')` + cronMultiCmd = ` +for pod in $pods; do + kubectl exec -i $pod -c %s -- sh -c '%s' +done` + cronSingle = `pod=$(kubectl get pods --selector=%s/component=%s,%s/resource=deployment -o jsonpath='{.items[0].metadata.name}')` + cronCmd = ` +kubectl exec -i $pod -c %s -- sh -c '%s'` +) + +type CronDef struct { + Command string `yaml:"command"` + Schedule string `yaml:"schedule"` + Multi bool `yaml:"allPods,omitempty"` +} + +func buildCrontab(deployName string, deployment *helm.Deployment, s *types.ServiceConfig, fileGeneratorChan HelmFileGenerator) { + // get the cron label from the service + var crondef string + var ok bool + if crondef, ok = s.Labels[helm.LABEL_CRON]; !ok { + return + } + + // parse yaml + crons := []CronDef{} + err := yaml.Unmarshal([]byte(crondef), &crons) + if err != nil { + log.Fatalf("error: %v", err) + } + log.Println(crons) + + // create a serviceAccount + sa := helm.NewServiceAccount(deployName) + // create a role + role := helm.NewCronRole(deployName) + + // create a roleBinding + roleBinding := helm.NewRoleBinding(deployName, sa, role) + + // make generation + fileGeneratorChan <- sa + fileGeneratorChan <- role + fileGeneratorChan <- roleBinding + + // create crontabs + for _, cron := range crons { + var cmd, podget string + if cron.Multi { + podget = cronMulti + cmd = cronMultiCmd + } else { + podget = cronSingle + cmd = cronCmd + } + podget = fmt.Sprintf(podget, helm.K, deployName, helm.K) + cmd = fmt.Sprintf(cmd, s.Name, cron.Command) + cmd = podget + cmd + + cronTab := helm.NewCrontab( + deployName, + "bitnami/kubectl", + cmd, + cron.Schedule, + sa, + ) + // add crontab + fileGeneratorChan <- cronTab + } + + return +} diff --git a/generator/main.go b/generator/main.go index b2fe43f..542894a 100644 --- a/generator/main.go +++ b/generator/main.go @@ -788,7 +788,14 @@ func setSecretVar(name string, s *types.ServiceConfig, c *helm.Container) *helm. // Generate a container in deployment with all needed objects (volumes, secrets, env, ...). // The deployName shoud be the name of the deployment, we cannot get it from Metadata as this is a variable name. -func newContainerForDeployment(deployName, containerName string, deployment *helm.Deployment, s *types.ServiceConfig, fileGeneratorChan HelmFileGenerator) *helm.Container { +func newContainerForDeployment( + deployName, containerName string, + deployment *helm.Deployment, + s *types.ServiceConfig, + fileGeneratorChan HelmFileGenerator) *helm.Container { + + buildCrontab(deployName, deployment, s, fileGeneratorChan) + container := helm.NewContainer(containerName, s.Image, s.Environment, s.Labels) applyEnvMapLabel(s, container) diff --git a/helm/cronTab.go b/helm/cronTab.go new file mode 100644 index 0000000..5ac8d3d --- /dev/null +++ b/helm/cronTab.go @@ -0,0 +1,60 @@ +package helm + +type Job struct { + ServiceAccount string `yaml:"serviceAccount,omitempty"` + ServiceAccountName string `yaml:"serviceAccountName,omitempty"` + Containers []Container `yaml:"containers"` + RestartPolicy string `yaml:"restartPolicy,omitempty"` +} +type JobSpec struct { + Template Job `yaml:"template"` +} + +type JobTemplate struct { + Metadata Metadata `yaml:"metadata"` + Spec JobSpec `yaml:"spec"` + Schedule string `yaml:"schedule"` +} + +type CronTab struct { + *K8sBase `yaml:",inline"` + JobTemplate JobTemplate `yaml:"jobTemplate"` +} + +func NewCrontab(name, image, command, schedule string, serviceAccount *ServiceAccount) *CronTab { + cron := &CronTab{ + K8sBase: NewBase(), + } + cron.K8sBase.ApiVersion = "batch/v1" + cron.K8sBase.Kind = "CronJob" + + //cmd, err := shlex.Split(command) + //if err != nil { + // panic(err) + //} + + cron.K8sBase.Metadata.Name = ReleaseNameTpl + "-" + name + cron.K8sBase.Metadata.Labels[K+"/component"] = name + cron.JobTemplate = JobTemplate{ + Schedule: schedule, + Metadata: Metadata{ + Labels: cron.K8sBase.Metadata.Labels, + }, + Spec: JobSpec{ + Template: Job{ + ServiceAccount: serviceAccount.Name(), + ServiceAccountName: serviceAccount.Name(), + Containers: []Container{ + { + Name: name, + Image: image, + Command: []string{command}, + }, + }, + RestartPolicy: "OnFailure", + }, + }, + } + + return cron +} diff --git a/helm/deployment.go b/helm/deployment.go index d1dec8a..3943423 100644 --- a/helm/deployment.go +++ b/helm/deployment.go @@ -12,6 +12,7 @@ func NewDeployment(name string) *Deployment { d.K8sBase.ApiVersion = "apps/v1" d.K8sBase.Kind = "Deployment" d.K8sBase.Metadata.Labels[K+"/component"] = name + d.K8sBase.Metadata.Labels[K+"/resource"] = "deployment" return d } diff --git a/helm/labels.go b/helm/labels.go index d9fc397..844d070 100644 --- a/helm/labels.go +++ b/helm/labels.go @@ -18,6 +18,7 @@ const ( LABEL_EMPTYDIRS = K + "/empty-dirs" LABEL_IGNORE = K + "/ignore" LABEL_SECRETVARS = K + "/secret-vars" + LABEL_CRON = K + "/crontabs" //deprecated: use LABEL_MAP_ENV instead LABEL_ENV_SERVICE = K + "/env-to-service" @@ -37,6 +38,7 @@ func GetLabelsDocumentation() string { {{.LABEL_SAMEPOD | printf "%-33s"}}: specifies that the pod should be deployed in the same pod than the given service name {{.LABEL_VOLUMEFROM | printf "%-33s"}}: specifies that the volumes to be mounted from the given service (yaml style) {{.LABEL_EMPTYDIRS | printf "%-33s"}}: specifies that the given volume names should be "emptyDir" instead of persistentVolumeClaim (coma separated) +{{.LABEL_CRON | printf "%-33s"}}: specifies that the given cronjobs should be deployed (yaml style, array) {{.LABEL_HEALTHCHECK | printf "%-33s"}}: specifies that the container should be monitored by a healthcheck, **it overrides the docker-compose healthcheck**. {{ printf "%-34s" ""}} You can use these form of label values: {{ printf "%-35s" ""}}- "http://[not used address][:port][/path]" to specify an http healthcheck @@ -56,6 +58,7 @@ func GetLabelsDocumentation() string { "LABEL_IGNORE": LABEL_IGNORE, "LABEL_MAP_ENV": LABEL_MAP_ENV, "LABEL_SECRETVARS": LABEL_SECRETVARS, + "LABEL_CRON": LABEL_CRON, }) return buff.String() } diff --git a/helm/role.go b/helm/role.go new file mode 100644 index 0000000..152d1d0 --- /dev/null +++ b/helm/role.go @@ -0,0 +1,33 @@ +package helm + +type Rule struct { + ApiGroup []string `yaml:"apiGroup,omitempty"` + Resources []string `yaml:"resource,omitempty"` + Verbs []string `yaml:"verbs,omitempty"` +} + +type Role struct { + *K8sBase `yaml:",inline"` + Rules []Rule `yaml:"rules,omitempty"` +} + +func NewCronRole(name string) *Role { + role := &Role{ + K8sBase: NewBase(), + } + + role.K8sBase.Metadata.Name = ReleaseNameTpl + "-" + name + "-cron-executor" + role.K8sBase.Kind = "Role" + role.K8sBase.ApiVersion = "rbac.authorization.k8s.io/v1" + role.K8sBase.Metadata.Labels[K+"/component"] = name + + role.Rules = []Rule{ + { + ApiGroup: []string{""}, + Resources: []string{"pods", "pods/exec"}, + Verbs: []string{"get", "list", "create"}, + }, + } + + return role +} diff --git a/helm/roleBinding.go b/helm/roleBinding.go new file mode 100644 index 0000000..a99d8ef --- /dev/null +++ b/helm/roleBinding.go @@ -0,0 +1,44 @@ +package helm + +type RoleRef struct { + Kind string `yaml:"kind"` + Name string `yaml:"name"` + APIGroup string `yaml:"apiGroup"` +} + +type Subject struct { + Kind string `yaml:"kind"` + Name string `yaml:"name"` + Namespace string `yaml:"namespace"` +} + +type RoleBinding struct { + *K8sBase `yaml:",inline"` + RoleRef RoleRef `yaml:"roleRef,omitempty"` + Subjects []Subject `yaml:"subjects,omitempty"` +} + +func NewRoleBinding(name string, user *ServiceAccount, role *Role) *RoleBinding { + rb := &RoleBinding{ + K8sBase: NewBase(), + } + + rb.K8sBase.Kind = "RoleBinding" + rb.K8sBase.Metadata.Name = ReleaseNameTpl + "-" + name + "-cron-allow" + rb.K8sBase.ApiVersion = "rbac.authorization.k8s.io/v1" + rb.K8sBase.Metadata.Labels[K+"/component"] = name + + rb.RoleRef.Kind = "Role" + rb.RoleRef.Name = role.Metadata.Name + rb.RoleRef.APIGroup = "rbac.authorization.k8s.io" + + rb.Subjects = []Subject{ + { + Kind: "ServiceAccount", + Name: user.Metadata.Name, + Namespace: "{{ .Release.Namespace }}", + }, + } + + return rb +} diff --git a/helm/serviceAccount.go b/helm/serviceAccount.go new file mode 100644 index 0000000..e7b44c5 --- /dev/null +++ b/helm/serviceAccount.go @@ -0,0 +1,18 @@ +package helm + +// ServiceAccount defines a service account +type ServiceAccount struct { + *K8sBase `yaml:",inline"` +} + +// NewServiceAccount creates a new service account with a given name. +func NewServiceAccount(name string) *ServiceAccount { + sa := &ServiceAccount{ + K8sBase: NewBase(), + } + sa.K8sBase.Kind = "ServiceAccount" + sa.K8sBase.ApiVersion = "v1" + sa.K8sBase.Metadata.Name = ReleaseNameTpl + "-" + name + "-cron-user" + sa.K8sBase.Metadata.Labels[K+"/component"] = name + return sa +} -- 2.49.1 From 124f06e5f40479a25ba48c803a81ae0ca401578f Mon Sep 17 00:00:00 2001 From: Patrice Ferlet Date: Tue, 24 May 2022 15:21:08 +0200 Subject: [PATCH 02/26] WIP - make better generation for crontabs --- generator/crontabs.go | 25 ++++++++++++++++++++++--- generator/main.go | 2 ++ helm/labels.go | 21 +++++++++++++-------- 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/generator/crontabs.go b/generator/crontabs.go index adf7006..e8f76b5 100644 --- a/generator/crontabs.go +++ b/generator/crontabs.go @@ -3,6 +3,7 @@ package generator import ( "fmt" "katenary/helm" + "katenary/logger" "log" "github.com/compose-spec/compose-go/types" @@ -23,6 +24,7 @@ kubectl exec -i $pod -c %s -- sh -c '%s'` type CronDef struct { Command string `yaml:"command"` Schedule string `yaml:"schedule"` + Image string `yaml:"image"` Multi bool `yaml:"allPods,omitempty"` } @@ -40,7 +42,6 @@ func buildCrontab(deployName string, deployment *helm.Deployment, s *types.Servi if err != nil { log.Fatalf("error: %v", err) } - log.Println(crons) // create a serviceAccount sa := helm.NewServiceAccount(deployName) @@ -51,10 +52,13 @@ func buildCrontab(deployName string, deployment *helm.Deployment, s *types.Servi roleBinding := helm.NewRoleBinding(deployName, sa, role) // make generation + logger.Magenta(ICON_RBAC, "Generating ServiceAccount, Role and RoleBinding for cron jobs", deployName) fileGeneratorChan <- sa fileGeneratorChan <- role fileGeneratorChan <- roleBinding + index := len(crons) - 1 // will be 0 when there is only one cron - made to name crons + // create crontabs for _, cron := range crons { var cmd, podget string @@ -69,14 +73,29 @@ func buildCrontab(deployName string, deployment *helm.Deployment, s *types.Servi cmd = fmt.Sprintf(cmd, s.Name, cron.Command) cmd = podget + cmd + if cron.Image == "" { + cron.Image = "bitnami/kubectl" + } + + name := deployName + if index > 0 { + name = fmt.Sprintf("%s-%d", deployName, index) + index++ + } + cronTab := helm.NewCrontab( - deployName, - "bitnami/kubectl", + name, + cron.Image, cmd, cron.Schedule, sa, ) // add crontab + suffix := "" + if index > 0 { + suffix = fmt.Sprintf("%d", index) + } + logger.Magenta(ICON_CRON, "Generating crontab", deployName, suffix) fileGeneratorChan <- cronTab } diff --git a/generator/main.go b/generator/main.go index 542894a..ced2a20 100644 --- a/generator/main.go +++ b/generator/main.go @@ -28,6 +28,8 @@ const ( ICON_CONF = "📝" ICON_STORE = "⚡" ICON_INGRESS = "🌐" + ICON_RBAC = "🔑" + ICON_CRON = "🕒" ) // Values is kept in memory to create a values.yaml file. diff --git a/helm/labels.go b/helm/labels.go index 844d070..53d41df 100644 --- a/helm/labels.go +++ b/helm/labels.go @@ -28,22 +28,27 @@ const ( func GetLabelsDocumentation() string { t, _ := template.New("labels").Parse(` # Labels -{{.LABEL_IGNORE | printf "%-33s"}}: ignore the container, it will not yied any object in the helm chart -{{.LABEL_SECRETVARS | printf "%-33s"}}: secret variables to push on a secret file -{{.LABEL_ENV_SECRET | printf "%-33s"}}: set the given file names as a secret instead of configmap -{{.LABEL_MAP_ENV | printf "%-33s"}}: map environment variable to a template string (yaml style) +{{.LABEL_IGNORE | printf "%-33s"}}: ignore the container, it will not yied any object in the helm chart (bool) +{{.LABEL_SECRETVARS | printf "%-33s"}}: secret variables to push on a secret file (coma separated) +{{.LABEL_ENV_SECRET | printf "%-33s"}}: set the given file names as a secret instead of configmap (coma separated) +{{.LABEL_MAP_ENV | printf "%-33s"}}: map environment variable to a template string (yaml style, object) {{.LABEL_PORT | printf "%-33s"}}: set the ports to expose as a service (coma separated) {{.LABEL_INGRESS | printf "%-33s"}}: set the port to expose in an ingress (coma separated) {{.LABEL_VOL_CM | printf "%-33s"}}: specifies that the volumes points on a configmap (coma separated) -{{.LABEL_SAMEPOD | printf "%-33s"}}: specifies that the pod should be deployed in the same pod than the given service name +{{.LABEL_SAMEPOD | printf "%-33s"}}: specifies that the pod should be deployed in the same pod than the given service name (string) {{.LABEL_VOLUMEFROM | printf "%-33s"}}: specifies that the volumes to be mounted from the given service (yaml style) {{.LABEL_EMPTYDIRS | printf "%-33s"}}: specifies that the given volume names should be "emptyDir" instead of persistentVolumeClaim (coma separated) {{.LABEL_CRON | printf "%-33s"}}: specifies that the given cronjobs should be deployed (yaml style, array) +{{ printf "%-34s" ""}} The form is the following: +{{ printf "%-34s" ""}} - command: the command to run +{{ printf "%-34s" ""}} schedule: the schedule to run the command (e.g. "@daily" or "*/1 * * * *") +{{ printf "%-34s" ""}} image: the image to use for the command (default to "bitnami/kubectl") +{{ printf "%-34s" ""}} allPods: true if you want to run the command on all pods (default to false) {{.LABEL_HEALTHCHECK | printf "%-33s"}}: specifies that the container should be monitored by a healthcheck, **it overrides the docker-compose healthcheck**. {{ printf "%-34s" ""}} You can use these form of label values: -{{ printf "%-35s" ""}}- "http://[not used address][:port][/path]" to specify an http healthcheck -{{ printf "%-35s" ""}}- "tcp://[not used address]:port" to specify a tcp healthcheck -{{ printf "%-35s" ""}}- other string is condidered as a "command" healthcheck +{{ printf "%-35s" ""}} "http://[not used address][:port][/path]" to specify an http healthcheck +{{ printf "%-35s" ""}} "tcp://[not used address]:port" to specify a tcp healthcheck +{{ printf "%-35s" ""}} other string is condidered as a "command" healthcheck `) buff := bytes.NewBuffer(nil) t.Execute(buff, map[string]string{ -- 2.49.1 From 4bc252dfe8d25e03b3a7317b76a6fc7369787309 Mon Sep 17 00:00:00 2001 From: Patrice Ferlet Date: Tue, 24 May 2022 15:28:49 +0200 Subject: [PATCH 03/26] Adapt labels --- helm/labels.go | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/helm/labels.go b/helm/labels.go index 53d41df..0af9d03 100644 --- a/helm/labels.go +++ b/helm/labels.go @@ -26,8 +26,7 @@ const ( // GetLabelsDocumentation returns the documentation for the labels. func GetLabelsDocumentation() string { - t, _ := template.New("labels").Parse(` -# Labels + t, _ := template.New("labels").Parse(`# Labels {{.LABEL_IGNORE | printf "%-33s"}}: ignore the container, it will not yied any object in the helm chart (bool) {{.LABEL_SECRETVARS | printf "%-33s"}}: secret variables to push on a secret file (coma separated) {{.LABEL_ENV_SECRET | printf "%-33s"}}: set the given file names as a secret instead of configmap (coma separated) @@ -35,21 +34,24 @@ func GetLabelsDocumentation() string { {{.LABEL_PORT | printf "%-33s"}}: set the ports to expose as a service (coma separated) {{.LABEL_INGRESS | printf "%-33s"}}: set the port to expose in an ingress (coma separated) {{.LABEL_VOL_CM | printf "%-33s"}}: specifies that the volumes points on a configmap (coma separated) -{{.LABEL_SAMEPOD | printf "%-33s"}}: specifies that the pod should be deployed in the same pod than the given service name (string) +{{.LABEL_SAMEPOD | printf "%-33s"}}: specifies that the pod should be deployed in the same pod than the +{{ printf "%-34s" ""}} given service name (string) {{.LABEL_VOLUMEFROM | printf "%-33s"}}: specifies that the volumes to be mounted from the given service (yaml style) -{{.LABEL_EMPTYDIRS | printf "%-33s"}}: specifies that the given volume names should be "emptyDir" instead of persistentVolumeClaim (coma separated) -{{.LABEL_CRON | printf "%-33s"}}: specifies that the given cronjobs should be deployed (yaml style, array) +{{.LABEL_EMPTYDIRS | printf "%-33s"}}: specifies that the given volume names should be "emptyDir" instead of +{{ printf "%-34s" ""}} persistentVolumeClaim (coma separated) +{{.LABEL_CRON | printf "%-33s"}}: specifies a cronjobs to create (yaml style, array) - this will create a +{{ printf "%-34s" ""}} cronjob, a service account, a role and a rolebinding to start the command with "kubectl" {{ printf "%-34s" ""}} The form is the following: {{ printf "%-34s" ""}} - command: the command to run {{ printf "%-34s" ""}} schedule: the schedule to run the command (e.g. "@daily" or "*/1 * * * *") {{ printf "%-34s" ""}} image: the image to use for the command (default to "bitnami/kubectl") {{ printf "%-34s" ""}} allPods: true if you want to run the command on all pods (default to false) -{{.LABEL_HEALTHCHECK | printf "%-33s"}}: specifies that the container should be monitored by a healthcheck, **it overrides the docker-compose healthcheck**. +{{.LABEL_HEALTHCHECK | printf "%-33s"}}: specifies that the container should be monitored by a healthcheck, +{{ printf "%-34s" ""}} **it overrides the docker-compose healthcheck**. {{ printf "%-34s" ""}} You can use these form of label values: -{{ printf "%-35s" ""}} "http://[not used address][:port][/path]" to specify an http healthcheck -{{ printf "%-35s" ""}} "tcp://[not used address]:port" to specify a tcp healthcheck -{{ printf "%-35s" ""}} other string is condidered as a "command" healthcheck - `) +{{ printf "%-35s" ""}} -> http://[ignored][:port][/path] to specify an http healthcheck +{{ printf "%-35s" ""}} -> tcp://[ignored]:port to specify a tcp healthcheck +{{ printf "%-35s" ""}} -> other string is condidered as a "command" healthcheck`) buff := bytes.NewBuffer(nil) t.Execute(buff, map[string]string{ "LABEL_ENV_SECRET": LABEL_ENV_SECRET, -- 2.49.1 From 99fbff6761fe09edcee5777b1cc9d6cb134d0d6a Mon Sep 17 00:00:00 2001 From: Patrice Ferlet Date: Tue, 24 May 2022 15:32:38 +0200 Subject: [PATCH 04/26] Fix doc to include new label --- README.md | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index dd410a6..4968da4 100644 --- a/README.md +++ b/README.md @@ -173,20 +173,32 @@ services: These labels could be found by `katenary show-labels`, and can be placed as "labels" inside your docker-compose file: ``` -katenary.io/ignore : ignore the container, it will not yied any object in the helm chart -katenary.io/secret-vars : secret variables to push on a secret file -katenary.io/secret-envfiles : set the given file names as a secret instead of configmap -katenary.io/mapenv : map environment variable to a template string (yaml style) +# Labels +katenary.io/ignore : ignore the container, it will not yied any object in the helm chart (bool) +katenary.io/secret-vars : secret variables to push on a secret file (coma separated) +katenary.io/secret-envfiles : set the given file names as a secret instead of configmap (coma separated) +katenary.io/mapenv : map environment variable to a template string (yaml style, object) katenary.io/ports : set the ports to expose as a service (coma separated) katenary.io/ingress : set the port to expose in an ingress (coma separated) katenary.io/configmap-volumes : specifies that the volumes points on a configmap (coma separated) -katenary.io/same-pod : specifies that the pod should be deployed in the same pod than the given service name -katenary.io/empty-dirs : specifies that the given volume names should be "emptyDir" instead of persistentVolumeClaim (coma separated) -katenary.io/healthcheck : specifies that the container should be monitored by a healthcheck, **it overrides the docker-compose healthcheck**. +katenary.io/same-pod : specifies that the pod should be deployed in the same pod than the + given service name (string) +katenary.io/volume-from : specifies that the volumes to be mounted from the given service (yaml style) +katenary.io/empty-dirs : specifies that the given volume names should be "emptyDir" instead of + persistentVolumeClaim (coma separated) +katenary.io/crontabs : specifies a cronjobs to create (yaml style, array) - this will create a + cronjob, a service account, a role and a rolebinding to start the command with "kubectl" + The form is the following: + - command: the command to run + schedule: the schedule to run the command (e.g. "@daily" or "*/1 * * * *") + image: the image to use for the command (default to "bitnami/kubectl") + allPods: true if you want to run the command on all pods (default to false) +katenary.io/healthcheck : specifies that the container should be monitored by a healthcheck, + **it overrides the docker-compose healthcheck**. You can use these form of label values: - - "http://[not used address][:port][/path]" to specify an http healthcheck - - "tcp://[not used address]:port" to specify a tcp healthcheck - - other string is condidered as a "command" healthcheck + -> http://[ignored][:port][/path] to specify an http healthcheck + -> tcp://[ignored]:port to specify a tcp healthcheck + -> other string is condidered as a "command" healthcheck ``` # What a name... -- 2.49.1 From 52d19a2558e4b9de02b53db6c08835e7728d8fc3 Mon Sep 17 00:00:00 2001 From: Patrice Ferlet Date: Tue, 24 May 2022 15:37:31 +0200 Subject: [PATCH 05/26] Fix english... --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 4968da4..117be07 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ Katenary Logo -Katenary is a tool to help transforming `docker-compose` files to a working Helm Chart for Kubernetes. +Katenary is a tool to help to transform `docker-compose` files to a working Helm Chart for Kubernetes. -> **Important Note:** Katenary is a tool to help building Helm Chart from a docker-compose file, but docker-compose doesn't propose as many features as what can do Kubernetes. So, we strongly recommend to use Katenary as a "bootstrap" tool and then to manually enhance the generated helm chart. +> **Important Note:** Katenary is a tool to help to build Helm Chart from a docker-compose file, but docker-compose doesn't propose as many features as what can do Kubernetes. So, we strongly recommend to use Katenary as a "bootstrap" tool and then to manually enhance the generated helm chart. This project is partially made at [Smile](https://www.smile.eu) @@ -59,7 +59,7 @@ Then place the `katenary` binary file inside your PATH. We strongly recommand to add the "completion" call to you SHELL using the common bashrc, or whatever the profile file you use. -E.g. : +E.g.: ```bash # bash in ~/.bashrc file @@ -122,11 +122,11 @@ What can be interpreted by Katenary: - Services with "image" section (cannot work with "build" section) - **Named Volumes** are transformed to persistent volume claims - note that local volume will break the transformation to Helm Chart because there is (for now) no way to make it working (see below for resolution) - if `ports` and/or `expose` section, katenary will create Services and bind the port to the corresponding container port -- `depends_on` will add init containers to wait for the depending service (using the first port) -- `env_file` list will create a configMap object per environemnt file (⚠ todo: the "to-service" label doesn't work with configMap for now) +- `depends_on` will add init containers to wait for the depending on service (using the first port) +- `env_file` list will create a configMap object per environemnt file (⚠ to-do: the "to-service" label doesn't work with configMap for now) - some labels can help to bind values, for example: - - `katenary.io/ingress: 80` will expose the port 80 in a ingress - - `katenary.io/mapenv: |`: allow to map environment to something else than the given value in the compose file + - `katenary.io/ingress: 80` will expose the port 80 in an ingress + - `katenary.io/mapenv: |`: allow mapping environment to something else than the given value in the compose file Exemple of a possible `docker-compose.yaml` file: -- 2.49.1 From 58e5fe05f8ae0eb87a48c0f8749735190cc66f87 Mon Sep 17 00:00:00 2001 From: Patrice Ferlet Date: Wed, 25 May 2022 07:46:36 +0200 Subject: [PATCH 06/26] Return when `len(crons) == 0` --- generator/crontabs.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/generator/crontabs.go b/generator/crontabs.go index e8f76b5..4066866 100644 --- a/generator/crontabs.go +++ b/generator/crontabs.go @@ -43,6 +43,10 @@ func buildCrontab(deployName string, deployment *helm.Deployment, s *types.Servi log.Fatalf("error: %v", err) } + if len(crons) == 0 { + return + } + // create a serviceAccount sa := helm.NewServiceAccount(deployName) // create a role -- 2.49.1 From 993ba719feb9fcf65e28272e7c482025c0424290 Mon Sep 17 00:00:00 2001 From: Patrice Ferlet Date: Wed, 1 Jun 2022 15:55:06 +0200 Subject: [PATCH 07/26] Fix cronjob structure --- helm/cronTab.go | 65 ++++++++++++++++++++++++++----------------------- 1 file changed, 34 insertions(+), 31 deletions(-) diff --git a/helm/cronTab.go b/helm/cronTab.go index 5ac8d3d..2f41112 100644 --- a/helm/cronTab.go +++ b/helm/cronTab.go @@ -1,25 +1,32 @@ package helm +type CronTab struct { + *K8sBase `yaml:",inline"` + Spec CronSpec `yaml:"spec"` +} +type CronSpec struct { + Schedule string `yaml:"schedule"` + JobTemplate JobTemplate `yaml:"jobTemplate"` +} +type JobTemplate struct { + Spec JobSpecDescription `yaml:"spec"` +} + +type JobSpecDescription struct { + Template JobSpecTemplate `yaml:"template"` +} + +type JobSpecTemplate struct { + Metadata Metadata `yaml:"metadata"` + Spec Job `yaml:"spec"` +} + type Job struct { ServiceAccount string `yaml:"serviceAccount,omitempty"` ServiceAccountName string `yaml:"serviceAccountName,omitempty"` Containers []Container `yaml:"containers"` RestartPolicy string `yaml:"restartPolicy,omitempty"` } -type JobSpec struct { - Template Job `yaml:"template"` -} - -type JobTemplate struct { - Metadata Metadata `yaml:"metadata"` - Spec JobSpec `yaml:"spec"` - Schedule string `yaml:"schedule"` -} - -type CronTab struct { - *K8sBase `yaml:",inline"` - JobTemplate JobTemplate `yaml:"jobTemplate"` -} func NewCrontab(name, image, command, schedule string, serviceAccount *ServiceAccount) *CronTab { cron := &CronTab{ @@ -35,25 +42,21 @@ func NewCrontab(name, image, command, schedule string, serviceAccount *ServiceAc cron.K8sBase.Metadata.Name = ReleaseNameTpl + "-" + name cron.K8sBase.Metadata.Labels[K+"/component"] = name - cron.JobTemplate = JobTemplate{ - Schedule: schedule, - Metadata: Metadata{ - Labels: cron.K8sBase.Metadata.Labels, - }, - Spec: JobSpec{ - Template: Job{ - ServiceAccount: serviceAccount.Name(), - ServiceAccountName: serviceAccount.Name(), - Containers: []Container{ - { - Name: name, - Image: image, - Command: []string{command}, - }, - }, - RestartPolicy: "OnFailure", + cron.Spec.Schedule = schedule + cron.Spec.JobTemplate.Spec.Template.Metadata = Metadata{ + Labels: cron.K8sBase.Metadata.Labels, + } + cron.Spec.JobTemplate.Spec.Template.Spec = Job{ + ServiceAccount: serviceAccount.Name(), + ServiceAccountName: serviceAccount.Name(), + Containers: []Container{ + { + Name: name, + Image: image, + Command: []string{command}, }, }, + RestartPolicy: "OnFailure", } return cron -- 2.49.1 From aaf0e3e9ed6d7553941dfc9238fb9f31e63ff6e7 Mon Sep 17 00:00:00 2001 From: Patrice Ferlet Date: Wed, 1 Jun 2022 16:03:38 +0200 Subject: [PATCH 08/26] Cleanup --- helm/cronTab.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/helm/cronTab.go b/helm/cronTab.go index 2f41112..5316503 100644 --- a/helm/cronTab.go +++ b/helm/cronTab.go @@ -35,11 +35,6 @@ func NewCrontab(name, image, command, schedule string, serviceAccount *ServiceAc cron.K8sBase.ApiVersion = "batch/v1" cron.K8sBase.Kind = "CronJob" - //cmd, err := shlex.Split(command) - //if err != nil { - // panic(err) - //} - cron.K8sBase.Metadata.Name = ReleaseNameTpl + "-" + name cron.K8sBase.Metadata.Labels[K+"/component"] = name cron.Spec.Schedule = schedule -- 2.49.1 From f08a6ec9a771c87775c08808471d2da82acedf52 Mon Sep 17 00:00:00 2001 From: Patrice Ferlet Date: Wed, 1 Jun 2022 16:05:34 +0200 Subject: [PATCH 09/26] Fix apiGroups and resources (need "s") --- helm/role.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/helm/role.go b/helm/role.go index 152d1d0..c708b21 100644 --- a/helm/role.go +++ b/helm/role.go @@ -1,8 +1,8 @@ package helm type Rule struct { - ApiGroup []string `yaml:"apiGroup,omitempty"` - Resources []string `yaml:"resource,omitempty"` + ApiGroup []string `yaml:"apiGroups,omitempty"` + Resources []string `yaml:"resources,omitempty"` Verbs []string `yaml:"verbs,omitempty"` } -- 2.49.1 From 403e7fb7e5402d57014c286ee0d9166331c7004f Mon Sep 17 00:00:00 2001 From: Patrice Ferlet Date: Wed, 1 Jun 2022 16:51:45 +0200 Subject: [PATCH 10/26] Fix path label --- generator/main.go | 13 +++++++------ generator/writer.go | 3 ++- helm/configAndSecretMap.go | 6 +++--- generator/utils.go => tools/path.go | 2 +- generator/utils_test.go => tools/path_test.go | 2 +- 5 files changed, 14 insertions(+), 12 deletions(-) rename generator/utils.go => tools/path.go (96%) rename generator/utils_test.go => tools/path_test.go (93%) diff --git a/generator/main.go b/generator/main.go index 1110542..e3471d7 100644 --- a/generator/main.go +++ b/generator/main.go @@ -6,6 +6,7 @@ import ( "katenary/compose" "katenary/helm" "katenary/logger" + "katenary/tools" "log" "net/url" "os" @@ -272,7 +273,7 @@ func buildConfigMapFromPath(name, path string) *helm.ConfigMap { files[filename] = string(c) } - cm := helm.NewConfigMap(name, GetRelPath(path)) + cm := helm.NewConfigMap(name, tools.GetRelPath(path)) cm.Data = files return cm } @@ -337,7 +338,7 @@ func prepareVolumes(deployment, name string, s *types.ServiceConfig, container * isConfigMap := false for _, cmVol := range configMapsVolumes { - if GetRelPath(volname) == cmVol { + if tools.GetRelPath(volname) == cmVol { isConfigMap = true break } @@ -366,10 +367,10 @@ func prepareVolumes(deployment, name string, s *types.ServiceConfig, container * // the volume is a path and it's explicitally asked to be a configmap in labels cm := buildConfigMapFromPath(name, volname) - cm.K8sBase.Metadata.Name = helm.ReleaseNameTpl + "-" + name + "-" + PathToName(volname) + cm.K8sBase.Metadata.Name = helm.ReleaseNameTpl + "-" + name + "-" + tools.PathToName(volname) // build a configmapRef for this volume - volname := PathToName(volname) + volname := tools.PathToName(volname) volumes = append(volumes, map[string]interface{}{ "name": volname, "configMap": map[string]string{ @@ -586,7 +587,7 @@ func prepareEnvFromFiles(name string, s *types.ServiceConfig, container *helm.Co // manage environment files (env_file in compose) for _, envfile := range s.EnvFile { - f := PathToName(envfile) + f := tools.PathToName(envfile) f = strings.ReplaceAll(f, ".env", "") isSecret := false for _, s := range secretsFiles { @@ -876,7 +877,7 @@ func addVolumeFrom(deployment *helm.Deployment, container *helm.Container, s *ty for name, volumes := range volumesFrom { for volumeName := range volumes { initianame := volumeName - volumeName = PathToName(volumeName) + volumeName = tools.PathToName(volumeName) // get the volume from the deployment container "name" var ctn *helm.Container for _, c := range deployment.Spec.Template.Spec.Containers { diff --git a/generator/writer.go b/generator/writer.go index 2cd0471..45a1fef 100644 --- a/generator/writer.go +++ b/generator/writer.go @@ -4,6 +4,7 @@ import ( "katenary/compose" "katenary/generator/writers" "katenary/helm" + "katenary/tools" "log" "os" "path/filepath" @@ -175,7 +176,7 @@ func Generate(p *compose.Parser, katernayVersion, appName, appVersion, chartVers // there could be several files, so let's force the filename name := c.(helm.Named).Name() + "-" + c.GetType() suffix := c.GetPathRessource() - suffix = PathToName(suffix) + suffix = tools.PathToName(suffix) name += suffix name = PrefixRE.ReplaceAllString(name, "") writers.BuildConfigMap(c, kind, n, name, templatesDir) diff --git a/helm/configAndSecretMap.go b/helm/configAndSecretMap.go index 9b5f6b2..5a3a7c6 100644 --- a/helm/configAndSecretMap.go +++ b/helm/configAndSecretMap.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "io/ioutil" + "katenary/tools" "strings" ) @@ -31,8 +32,7 @@ func NewConfigMap(name, path string) *ConfigMap { base.Metadata.Name = ReleaseNameTpl + "-" + name base.Metadata.Labels[K+"/component"] = name if path != "" { - //base.Metadata.Labels[K+"/path"] = path - base.Metadata.Labels[K+"/path"] = `{{ "` + path + `" | quote }}` + base.Metadata.Labels[K+"/path"] = tools.PathToName(path) } return &ConfigMap{ K8sBase: base, @@ -97,7 +97,7 @@ func NewSecret(name, path string) *Secret { base.Metadata.Name = ReleaseNameTpl + "-" + name base.Metadata.Labels[K+"/component"] = name if path != "" { - base.Metadata.Labels[K+"/path"] = `{{ "` + path + `" | quote }}` + base.Metadata.Labels[K+"/path"] = tools.PathToName(path) } return &Secret{ K8sBase: base, diff --git a/generator/utils.go b/tools/path.go similarity index 96% rename from generator/utils.go rename to tools/path.go index 5c24c70..016acae 100644 --- a/generator/utils.go +++ b/tools/path.go @@ -1,4 +1,4 @@ -package generator +package tools import ( "katenary/compose" diff --git a/generator/utils_test.go b/tools/path_test.go similarity index 93% rename from generator/utils_test.go rename to tools/path_test.go index f7c64e3..cbda344 100644 --- a/generator/utils_test.go +++ b/tools/path_test.go @@ -1,4 +1,4 @@ -package generator +package tools import ( "katenary/compose" -- 2.49.1 From ac2382735c756b3c12f7593e33cf838022095923 Mon Sep 17 00:00:00 2001 From: Patrice Ferlet Date: Wed, 1 Jun 2022 16:55:41 +0200 Subject: [PATCH 11/26] Add name to service port --- helm/service.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/helm/service.go b/helm/service.go index f1a7ec1..72983ba 100644 --- a/helm/service.go +++ b/helm/service.go @@ -1,5 +1,7 @@ package helm +import "strconv" + // Service is a Kubernetes service. type Service struct { *K8sBase `yaml:",inline"` @@ -24,6 +26,7 @@ type ServicePort struct { Protocol string `yaml:"protocol"` Port int `yaml:"port"` TargetPort int `yaml:"targetPort"` + Name string `yaml:"name"` } // NewServicePort creates a new initialized service port. @@ -32,6 +35,7 @@ func NewServicePort(port, target int) *ServicePort { Protocol: "TCP", Port: port, TargetPort: port, + Name: "port-" + strconv.Itoa(port), } } -- 2.49.1 From 1350a2616288d92c191d61123af12bf4292ed122 Mon Sep 17 00:00:00 2001 From: Patrice Ferlet Date: Wed, 1 Jun 2022 16:56:54 +0200 Subject: [PATCH 12/26] Add name to service port --- helm/service.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helm/service.go b/helm/service.go index 72983ba..78a0f78 100644 --- a/helm/service.go +++ b/helm/service.go @@ -35,7 +35,7 @@ func NewServicePort(port, target int) *ServicePort { Protocol: "TCP", Port: port, TargetPort: port, - Name: "port-" + strconv.Itoa(port), + Name: "port-" + strconv.Itoa(target), } } -- 2.49.1 From 4cf470f2228197af9747e0b2b307dfae54d77746 Mon Sep 17 00:00:00 2001 From: Patrice Ferlet Date: Fri, 3 Jun 2022 16:17:53 +0200 Subject: [PATCH 13/26] Allow several compose file in -c arg --- cmd/katenary/utils.go | 18 +++++++++++++----- compose/parser.go | 20 +++++++++++--------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/cmd/katenary/utils.go b/cmd/katenary/utils.go index bc0d8b4..9e179e5 100644 --- a/cmd/katenary/utils.go +++ b/cmd/katenary/utils.go @@ -98,14 +98,22 @@ func Convert(composeFile, appVersion, appName, chartDir, chartVersion string, fo fmt.Println("No compose file given") return } - _, err := os.Stat(composeFile) - if err != nil { - fmt.Println("No compose file found") - os.Exit(1) + + composeFiles := strings.Split(composeFile, ",") + for i, c := range composeFiles { + composeFiles[i] = strings.TrimSpace(c) + } + + for _, composeFile := range composeFiles { + _, err := os.Stat(composeFile) + if err != nil { + fmt.Println("Error reading file:", composeFile) + os.Exit(1) + } } // Parse the compose file now - p := compose.NewParser(composeFile) + p := compose.NewParser(composeFiles) p.Parse(appName) dirname := filepath.Join(chartDir, appName) diff --git a/compose/parser.go b/compose/parser.go index 86b0cb5..2e8a1b0 100644 --- a/compose/parser.go +++ b/compose/parser.go @@ -26,32 +26,34 @@ var ( ) // NewParser create a Parser and parse the file given in filename. If filename is empty, we try to parse the content[0] argument that should be a valid YAML content. -func NewParser(filename string, content ...string) *Parser { +func NewParser(filename []string, content ...string) *Parser { p := &Parser{} if len(content) > 0 { // mainly for the tests... - dir := filepath.Dir(filename) + dir := filepath.Dir(filename[0]) err := os.MkdirAll(dir, 0755) if err != nil { log.Fatal(err) } p.temporary = &dir - ioutil.WriteFile(filename, []byte(content[0]), 0644) - cli.DefaultFileNames = []string{filename} + ioutil.WriteFile(filename[0], []byte(content[0]), 0644) + cli.DefaultFileNames = filename } // if filename is not in cli Default files, add it if len(filename) > 0 { found := false - for _, f := range cli.DefaultFileNames { - if f == filename { - found = true - break + for _, defaultFileName := range cli.DefaultFileNames { + for _, givenFileName := range filename { + if defaultFileName == givenFileName { + found = true + break + } } } // add the file at first position if !found { - cli.DefaultFileNames = append([]string{filename}, cli.DefaultFileNames...) + cli.DefaultFileNames = append(filename, cli.DefaultFileNames...) } } -- 2.49.1 From 918baa2ce05a81f4d2f525f1581899be984da97e Mon Sep 17 00:00:00 2001 From: Patrice Ferlet Date: Fri, 3 Jun 2022 16:44:08 +0200 Subject: [PATCH 14/26] Set extended files as overrides config --- compose/parser.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/compose/parser.go b/compose/parser.go index 2e8a1b0..2653b5b 100644 --- a/compose/parser.go +++ b/compose/parser.go @@ -53,7 +53,10 @@ func NewParser(filename []string, content ...string) *Parser { } // add the file at first position if !found { - cli.DefaultFileNames = append(filename, cli.DefaultFileNames...) + cli.DefaultFileNames = append([]string{filename[0]}, cli.DefaultFileNames...) + } + if len(filename) > 1 { + cli.DefaultOverrideFileNames = append(filename[1:], cli.DefaultOverrideFileNames...) } } -- 2.49.1 From af05e41225349d0dc6d07f8ad6db1ba73d687369 Mon Sep 17 00:00:00 2001 From: Patrice Ferlet Date: Thu, 9 Jun 2022 09:20:12 +0200 Subject: [PATCH 15/26] Make "-c" (compose-file) argument repeated --- cmd/katenary/main.go | 4 ++-- cmd/katenary/utils.go | 7 +++---- generator/main_test.go | 2 +- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/cmd/katenary/main.go b/cmd/katenary/main.go index 712f057..de1ce08 100644 --- a/cmd/katenary/main.go +++ b/cmd/katenary/main.go @@ -61,9 +61,9 @@ func main() { "- if it's not defined, so the 0.0.1 version is used", Run: func(c *cobra.Command, args []string) { force := c.Flag("force").Changed + composeFile := c.Flags().StringArrayP("compose-file", "c", []string{}, "compose file to convert, can be use several times to override previous file (default: docker-compose.yml)") // TODO: is there a way to get typed values from cobra? appversion := c.Flag("app-version").Value.String() - composeFile := c.Flag("compose-file").Value.String() appName := c.Flag("app-name").Value.String() chartVersion := c.Flag("chart-version").Value.String() chartDir := c.Flag("output-dir").Value.String() @@ -71,7 +71,7 @@ func main() { if err != nil { writers.IndentSize = indentation } - Convert(composeFile, appversion, appName, chartDir, chartVersion, force) + Convert(*composeFile, appversion, appName, chartDir, chartVersion, force) }, } convertCmd.Flags().BoolP( diff --git a/cmd/katenary/utils.go b/cmd/katenary/utils.go index 9e179e5..2f6d071 100644 --- a/cmd/katenary/utils.go +++ b/cmd/katenary/utils.go @@ -93,15 +93,14 @@ func detectGitVersion() (string, error) { return defaulVersion, errors.New("git log failed") } -func Convert(composeFile, appVersion, appName, chartDir, chartVersion string, force bool) { +func Convert(composeFile []string, appVersion, appName, chartDir, chartVersion string, force bool) { if len(composeFile) == 0 { fmt.Println("No compose file given") return } - composeFiles := strings.Split(composeFile, ",") - for i, c := range composeFiles { - composeFiles[i] = strings.TrimSpace(c) + for i, c := range composeFile { + composeFile[i] = strings.TrimSpace(c) } for _, composeFile := range composeFiles { diff --git a/generator/main_test.go b/generator/main_test.go index 7a57006..c90ca39 100644 --- a/generator/main_test.go +++ b/generator/main_test.go @@ -127,7 +127,7 @@ func setUp(t *testing.T) (string, *compose.Parser) { } composefile := filepath.Join(tmpwork, "docker-compose.yaml") - p := compose.NewParser(composefile, DOCKER_COMPOSE_YML) + p := compose.NewParser([]string{composefile}, DOCKER_COMPOSE_YML) // create envfile for "useenvfile" service err = os.Mkdir(filepath.Join(tmpwork, "config"), 0777) -- 2.49.1 From e5d4ed1df594190149fdb8cda114c2a4f2ce8863 Mon Sep 17 00:00:00 2001 From: Patrice Ferlet Date: Thu, 9 Jun 2022 09:31:27 +0200 Subject: [PATCH 16/26] Fix compose-file argument usage --- cmd/katenary/main.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/cmd/katenary/main.go b/cmd/katenary/main.go index de1ce08..de1ff9b 100644 --- a/cmd/katenary/main.go +++ b/cmd/katenary/main.go @@ -5,6 +5,7 @@ import ( "katenary/generator/writers" "katenary/helm" "katenary/update" + "log" "strconv" "github.com/spf13/cobra" @@ -50,6 +51,7 @@ func main() { } // convert command, need some flags + var composeFiles *[]string convertCmd := &cobra.Command{ Use: "convert", Short: "Convert docker-compose to helm chart", @@ -61,8 +63,6 @@ func main() { "- if it's not defined, so the 0.0.1 version is used", Run: func(c *cobra.Command, args []string) { force := c.Flag("force").Changed - composeFile := c.Flags().StringArrayP("compose-file", "c", []string{}, "compose file to convert, can be use several times to override previous file (default: docker-compose.yml)") - // TODO: is there a way to get typed values from cobra? appversion := c.Flag("app-version").Value.String() appName := c.Flag("app-name").Value.String() chartVersion := c.Flag("chart-version").Value.String() @@ -71,17 +71,18 @@ func main() { if err != nil { writers.IndentSize = indentation } - Convert(*composeFile, appversion, appName, chartDir, chartVersion, force) + log.Println(composeFiles) + Convert(*composeFiles, appversion, appName, chartDir, chartVersion, force) }, } + composeFiles = convertCmd.Flags().StringArrayP( + "compose-file", "c", []string{ComposeFile}, "compose file to convert, can be use several times to override previous file. Order is important!") convertCmd.Flags().BoolP( "force", "f", false, "force overwrite of existing output files") convertCmd.Flags().StringP( "app-version", "a", AppVersion, "app version") convertCmd.Flags().StringP( "chart-version", "v", ChartVersion, "chart version") - convertCmd.Flags().StringP( - "compose-file", "c", ComposeFile, "docker compose file") convertCmd.Flags().StringP( "app-name", "n", AppName, "application name") convertCmd.Flags().StringP( -- 2.49.1 From 7203928d956f2a36d14833299eebe8afd8f50c68 Mon Sep 17 00:00:00 2001 From: Thomas BERNARD Date: Thu, 9 Jun 2022 18:35:04 +0200 Subject: [PATCH 17/26] Fix a typo (#22) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index dd410a6..0310947 100644 --- a/README.md +++ b/README.md @@ -193,7 +193,7 @@ katenary.io/healthcheck : specifies that the container should be monito Katenary is the stylized name of the project that comes from the "catenary" word. -A catenary is a curve formed by a wire, rope, or chain hanging freely from two points that are not in the same vertical line. For example, the anchor chain between a bot and the anchor. +A catenary is a curve formed by a wire, rope, or chain hanging freely from two points that are not in the same vertical line. For example, the anchor chain between a boat and the anchor. This "curved link" represents what we try to do, the project is a "streched link from docker-compose to helm chart". -- 2.49.1 From 5a8a1b135ed7249b6008977724cc1373bfffd215 Mon Sep 17 00:00:00 2001 From: Patrice Ferlet Date: Fri, 10 Jun 2022 09:47:15 +0200 Subject: [PATCH 18/26] Fixup the command line argument to get multiple compose files --- cmd/katenary/main.go | 2 -- cmd/katenary/utils.go | 14 ++++++-------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/cmd/katenary/main.go b/cmd/katenary/main.go index de1ff9b..ceaea8e 100644 --- a/cmd/katenary/main.go +++ b/cmd/katenary/main.go @@ -5,7 +5,6 @@ import ( "katenary/generator/writers" "katenary/helm" "katenary/update" - "log" "strconv" "github.com/spf13/cobra" @@ -71,7 +70,6 @@ func main() { if err != nil { writers.IndentSize = indentation } - log.Println(composeFiles) Convert(*composeFiles, appversion, appName, chartDir, chartVersion, force) }, } diff --git a/cmd/katenary/utils.go b/cmd/katenary/utils.go index 2f6d071..118932e 100644 --- a/cmd/katenary/utils.go +++ b/cmd/katenary/utils.go @@ -99,15 +99,13 @@ func Convert(composeFile []string, appVersion, appName, chartDir, chartVersion s return } - for i, c := range composeFile { - composeFile[i] = strings.TrimSpace(c) - } + composeFiles := composeFile + ComposeFile = composeFiles[0] - for _, composeFile := range composeFiles { - _, err := os.Stat(composeFile) - if err != nil { - fmt.Println("Error reading file:", composeFile) - os.Exit(1) + for _, cf := range composeFiles { + if _, err := os.Stat(cf); err != nil { + fmt.Printf("Compose file %s not found\n", cf) + return } } -- 2.49.1 From 8156f8cfa220776548afcb82e20c277c09b41238 Mon Sep 17 00:00:00 2001 From: Patrice Ferlet Date: Fri, 10 Jun 2022 11:33:09 +0200 Subject: [PATCH 19/26] Fixup jobs, roles... --- generator/crontabs.go | 2 +- generator/main.go | 1 + helm/cronTab.go | 12 +++++++++--- helm/deployment.go | 7 +++++++ helm/role.go | 9 +++++++-- 5 files changed, 25 insertions(+), 6 deletions(-) diff --git a/generator/crontabs.go b/generator/crontabs.go index 4066866..6eeb819 100644 --- a/generator/crontabs.go +++ b/generator/crontabs.go @@ -78,7 +78,7 @@ func buildCrontab(deployName string, deployment *helm.Deployment, s *types.Servi cmd = podget + cmd if cron.Image == "" { - cron.Image = "bitnami/kubectl" + cron.Image = "bitnami/kubectl:1.20" } name := deployName diff --git a/generator/main.go b/generator/main.go index e3471d7..a88dd56 100644 --- a/generator/main.go +++ b/generator/main.go @@ -74,6 +74,7 @@ func buildDeployment(name string, s *types.ServiceConfig, linked map[string]type // Add selectors selectors := buildSelector(name, s) + selectors[helm.K+"/resource"] = "deployment" deployment.Spec.Selector = map[string]interface{}{ "matchLabels": selectors, } diff --git a/helm/cronTab.go b/helm/cronTab.go index 5316503..ef79c63 100644 --- a/helm/cronTab.go +++ b/helm/cronTab.go @@ -5,8 +5,11 @@ type CronTab struct { Spec CronSpec `yaml:"spec"` } type CronSpec struct { - Schedule string `yaml:"schedule"` - JobTemplate JobTemplate `yaml:"jobTemplate"` + Schedule string `yaml:"schedule"` + JobTemplate JobTemplate `yaml:"jobTemplate"` + SuccessfulJobsHistoryLimit int `yaml:"successfulJobsHistoryLimit"` + FailedJobsHistoryLimit int `yaml:"failedJobsHistoryLimit"` + ConcurrencyPolicy string `yaml:"concurrencyPolicy"` } type JobTemplate struct { Spec JobSpecDescription `yaml:"spec"` @@ -38,6 +41,9 @@ func NewCrontab(name, image, command, schedule string, serviceAccount *ServiceAc cron.K8sBase.Metadata.Name = ReleaseNameTpl + "-" + name cron.K8sBase.Metadata.Labels[K+"/component"] = name cron.Spec.Schedule = schedule + cron.Spec.SuccessfulJobsHistoryLimit = 3 + cron.Spec.FailedJobsHistoryLimit = 3 + cron.Spec.ConcurrencyPolicy = "Forbid" cron.Spec.JobTemplate.Spec.Template.Metadata = Metadata{ Labels: cron.K8sBase.Metadata.Labels, } @@ -48,7 +54,7 @@ func NewCrontab(name, image, command, schedule string, serviceAccount *ServiceAc { Name: name, Image: image, - Command: []string{command}, + Command: []string{"sh", "-c", command}, }, }, RestartPolicy: "OnFailure", diff --git a/helm/deployment.go b/helm/deployment.go index 3943423..649d1db 100644 --- a/helm/deployment.go +++ b/helm/deployment.go @@ -25,6 +25,13 @@ type DepSpec struct { func NewDepSpec() *DepSpec { return &DepSpec{ Replicas: 1, + Template: PodTemplate{ + Metadata: Metadata{ + Labels: map[string]string{ + K + "/resource": "deployment", + }, + }, + }, } } diff --git a/helm/role.go b/helm/role.go index c708b21..111058a 100644 --- a/helm/role.go +++ b/helm/role.go @@ -24,8 +24,13 @@ func NewCronRole(name string) *Role { role.Rules = []Rule{ { ApiGroup: []string{""}, - Resources: []string{"pods", "pods/exec"}, - Verbs: []string{"get", "list", "create"}, + Resources: []string{"pods", "pods/log"}, + Verbs: []string{"get", "list", "watch", "create", "update", "patch", "delete"}, + }, + { + ApiGroup: []string{""}, + Resources: []string{"pods/exec"}, + Verbs: []string{"create"}, }, } -- 2.49.1 From a5036e1b80a3a09582c054299b855e6de808a61b Mon Sep 17 00:00:00 2001 From: Patrice Ferlet Date: Fri, 10 Jun 2022 12:10:18 +0200 Subject: [PATCH 20/26] Fix problem when several cron are declared --- generator/crontabs.go | 12 ++++++------ generator/writer.go | 4 +++- helm/cronTab.go | 22 ++++++++++++++-------- 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/generator/crontabs.go b/generator/crontabs.go index 6eeb819..6f0a3c1 100644 --- a/generator/crontabs.go +++ b/generator/crontabs.go @@ -78,7 +78,7 @@ func buildCrontab(deployName string, deployment *helm.Deployment, s *types.Servi cmd = podget + cmd if cron.Image == "" { - cron.Image = "bitnami/kubectl:1.20" + cron.Image = `bitnami/kubectl:{{ printf "%s.%s" .Capabilities.KubeVersion.Major .Capabilities.KubeVersion.Minor }}` } name := deployName @@ -87,6 +87,11 @@ func buildCrontab(deployName string, deployment *helm.Deployment, s *types.Servi index++ } + // add crontab + suffix := "" + if index > 0 { + suffix = fmt.Sprintf("%d", index) + } cronTab := helm.NewCrontab( name, cron.Image, @@ -94,11 +99,6 @@ func buildCrontab(deployName string, deployment *helm.Deployment, s *types.Servi cron.Schedule, sa, ) - // add crontab - suffix := "" - if index > 0 { - suffix = fmt.Sprintf("%d", index) - } logger.Magenta(ICON_CRON, "Generating crontab", deployName, suffix) fileGeneratorChan <- cronTab } diff --git a/generator/writer.go b/generator/writer.go index 45a1fef..ce334d1 100644 --- a/generator/writer.go +++ b/generator/writer.go @@ -182,7 +182,9 @@ func Generate(p *compose.Parser, katernayVersion, appName, appVersion, chartVers writers.BuildConfigMap(c, kind, n, name, templatesDir) default: - fname := filepath.Join(templatesDir, n+"."+kind+".yaml") + name := c.(helm.Named).Name() + "-" + c.GetType() + name = PrefixRE.ReplaceAllString(name, "") + fname := filepath.Join(templatesDir, name+".yaml") fp, err := os.Create(fname) if err != nil { log.Fatal(err) diff --git a/helm/cronTab.go b/helm/cronTab.go index ef79c63..ff1e454 100644 --- a/helm/cronTab.go +++ b/helm/cronTab.go @@ -50,15 +50,21 @@ func NewCrontab(name, image, command, schedule string, serviceAccount *ServiceAc cron.Spec.JobTemplate.Spec.Template.Spec = Job{ ServiceAccount: serviceAccount.Name(), ServiceAccountName: serviceAccount.Name(), - Containers: []Container{ - { - Name: name, - Image: image, - Command: []string{"sh", "-c", command}, - }, - }, - RestartPolicy: "OnFailure", + RestartPolicy: "OnFailure", + } + if command != "" { + cron.AddCommand(command, image, name) } return cron } + +// AddCommand adds a command to the cron job +func (c *CronTab) AddCommand(command, image, name string) { + container := Container{ + Name: name, + Image: image, + Command: []string{"sh", "-c", command}, + } + c.Spec.JobTemplate.Spec.Template.Spec.Containers = append(c.Spec.JobTemplate.Spec.Template.Spec.Containers, container) +} -- 2.49.1 From 8d0ab2ad0c722a556b2be932415a8ea28c25f832 Mon Sep 17 00:00:00 2001 From: Patrice Ferlet Date: Fri, 10 Jun 2022 13:42:30 +0200 Subject: [PATCH 21/26] Use dot, not "minus" for "kind" of file names --- generator/writer.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/generator/writer.go b/generator/writer.go index ce334d1..7542c94 100644 --- a/generator/writer.go +++ b/generator/writer.go @@ -174,7 +174,7 @@ func Generate(p *compose.Parser, katernayVersion, appName, appVersion, chartVers case *helm.ConfigMap, *helm.Secret: // there could be several files, so let's force the filename - name := c.(helm.Named).Name() + "-" + c.GetType() + name := c.(helm.Named).Name() + "." + c.GetType() suffix := c.GetPathRessource() suffix = tools.PathToName(suffix) name += suffix @@ -182,7 +182,7 @@ func Generate(p *compose.Parser, katernayVersion, appName, appVersion, chartVers writers.BuildConfigMap(c, kind, n, name, templatesDir) default: - name := c.(helm.Named).Name() + "-" + c.GetType() + name := c.(helm.Named).Name() + "." + c.GetType() name = PrefixRE.ReplaceAllString(name, "") fname := filepath.Join(templatesDir, name+".yaml") fp, err := os.Create(fname) -- 2.49.1 From 8860bb01be8db20b69a1a945c6f7ba29710bba5b Mon Sep 17 00:00:00 2001 From: Patrice Ferlet Date: Fri, 10 Jun 2022 13:50:14 +0200 Subject: [PATCH 22/26] At this time, ignore all yaml/yml files --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index e6c096f..91482e6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,8 @@ dist/* .cache/* chart/* -docker-compose.yaml +*.yaml +*.yml ./katenary *.env docker-compose* -- 2.49.1 From b7c867a9639287ab468074d12967cdc3e5044c96 Mon Sep 17 00:00:00 2001 From: Patrice Ferlet Date: Fri, 10 Jun 2022 14:36:03 +0200 Subject: [PATCH 23/26] Add containerPort label --- generator/main.go | 27 +++++++++++++++++ helm/labels.go | 77 ++++++++++++++++++++++++----------------------- 2 files changed, 67 insertions(+), 37 deletions(-) diff --git a/generator/main.go b/generator/main.go index a88dd56..46a80e2 100644 --- a/generator/main.go +++ b/generator/main.go @@ -852,6 +852,33 @@ func newContainerForDeployment( prepareInitContainers(containerName, s, container)..., ) + // check if there is containerPort assigned in label, add it, and do + // not create service for this. + if ports, ok := s.Labels[helm.LABEL_CONTAINER_PORT]; ok { + for _, port := range strings.Split(ports, ",") { + func(port string, container *helm.Container, s *types.ServiceConfig) { + port = strings.TrimSpace(port) + if port == "" { + return + } + portNumber, err := strconv.Atoi(port) + if err != nil { + return + } + // avoid already declared ports + for _, p := range s.Ports { + if int(p.Target) == portNumber { + return + } + } + container.Ports = append(container.Ports, &helm.ContainerPort{ + Name: deployName + "-" + port, + ContainerPort: portNumber, + }) + }(port, container, s) + } + } + return container } diff --git a/helm/labels.go b/helm/labels.go index 0af9d03..9bcf77d 100644 --- a/helm/labels.go +++ b/helm/labels.go @@ -7,18 +7,19 @@ import ( const ReleaseNameTpl = "{{ .Release.Name }}" const ( - LABEL_MAP_ENV = K + "/mapenv" - LABEL_ENV_SECRET = K + "/secret-envfiles" - LABEL_PORT = K + "/ports" - LABEL_INGRESS = K + "/ingress" - LABEL_VOL_CM = K + "/configmap-volumes" - LABEL_HEALTHCHECK = K + "/healthcheck" - LABEL_SAMEPOD = K + "/same-pod" - LABEL_VOLUMEFROM = K + "/volume-from" - LABEL_EMPTYDIRS = K + "/empty-dirs" - LABEL_IGNORE = K + "/ignore" - LABEL_SECRETVARS = K + "/secret-vars" - LABEL_CRON = K + "/crontabs" + LABEL_MAP_ENV = K + "/mapenv" + LABEL_ENV_SECRET = K + "/secret-envfiles" + LABEL_PORT = K + "/ports" + LABEL_CONTAINER_PORT = K + "/container-ports" + LABEL_INGRESS = K + "/ingress" + LABEL_VOL_CM = K + "/configmap-volumes" + LABEL_HEALTHCHECK = K + "/healthcheck" + LABEL_SAMEPOD = K + "/same-pod" + LABEL_VOLUMEFROM = K + "/volume-from" + LABEL_EMPTYDIRS = K + "/empty-dirs" + LABEL_IGNORE = K + "/ignore" + LABEL_SECRETVARS = K + "/secret-vars" + LABEL_CRON = K + "/crontabs" //deprecated: use LABEL_MAP_ENV instead LABEL_ENV_SERVICE = K + "/env-to-service" @@ -27,19 +28,20 @@ const ( // GetLabelsDocumentation returns the documentation for the labels. func GetLabelsDocumentation() string { t, _ := template.New("labels").Parse(`# Labels -{{.LABEL_IGNORE | printf "%-33s"}}: ignore the container, it will not yied any object in the helm chart (bool) -{{.LABEL_SECRETVARS | printf "%-33s"}}: secret variables to push on a secret file (coma separated) -{{.LABEL_ENV_SECRET | printf "%-33s"}}: set the given file names as a secret instead of configmap (coma separated) -{{.LABEL_MAP_ENV | printf "%-33s"}}: map environment variable to a template string (yaml style, object) -{{.LABEL_PORT | printf "%-33s"}}: set the ports to expose as a service (coma separated) -{{.LABEL_INGRESS | printf "%-33s"}}: set the port to expose in an ingress (coma separated) -{{.LABEL_VOL_CM | printf "%-33s"}}: specifies that the volumes points on a configmap (coma separated) -{{.LABEL_SAMEPOD | printf "%-33s"}}: specifies that the pod should be deployed in the same pod than the -{{ printf "%-34s" ""}} given service name (string) -{{.LABEL_VOLUMEFROM | printf "%-33s"}}: specifies that the volumes to be mounted from the given service (yaml style) -{{.LABEL_EMPTYDIRS | printf "%-33s"}}: specifies that the given volume names should be "emptyDir" instead of -{{ printf "%-34s" ""}} persistentVolumeClaim (coma separated) -{{.LABEL_CRON | printf "%-33s"}}: specifies a cronjobs to create (yaml style, array) - this will create a +{{.LABEL_IGNORE | printf "%-33s"}}: ignore the container, it will not yied any object in the helm chart (bool) +{{.LABEL_SECRETVARS | printf "%-33s"}}: secret variables to push on a secret file (coma separated) +{{.LABEL_ENV_SECRET | printf "%-33s"}}: set the given file names as a secret instead of configmap (coma separated) +contaienr in {{.LABEL_MAP_ENV | printf "%-33s"}}: map environment variable to a template string (yaml style, object) +{{.LABEL_PORT | printf "%-33s"}}: set the ports to assign on the container in pod + expose as a service (coma separated) +{{.LABEL_CONTAINER_PORT | printf "%-33s"}}: set the ports to assign on the contaienr in pod but avoid service (coma separated) +{{.LABEL_INGRESS | printf "%-33s"}}: set the port to expose in an ingress (coma separated) +{{.LABEL_VOL_CM | printf "%-33s"}}: specifies that the volumes points on a configmap (coma separated) +{{.LABEL_SAMEPOD | printf "%-33s"}}: specifies that the pod should be deployed in the same pod than the +{{ printf "%-34s" ""} } given service name (string) +{{.LABEL_VOLUMEFROM | printf "%-33s"}}: specifies that the volumes to be mounted from the given service (yaml style) +{{.LABEL_EMPTYDIRS | printf "%-33s"}}: specifies that the given volume names should be "emptyDir" instead of +{{ printf "%-34s" ""} } persistentVolumeClaim (coma separated) +{{.LABEL_CRON | printf "%-33s"}}: specifies a cronjobs to create (yaml style, array) - this will create a {{ printf "%-34s" ""}} cronjob, a service account, a role and a rolebinding to start the command with "kubectl" {{ printf "%-34s" ""}} The form is the following: {{ printf "%-34s" ""}} - command: the command to run @@ -54,18 +56,19 @@ func GetLabelsDocumentation() string { {{ printf "%-35s" ""}} -> other string is condidered as a "command" healthcheck`) buff := bytes.NewBuffer(nil) t.Execute(buff, map[string]string{ - "LABEL_ENV_SECRET": LABEL_ENV_SECRET, - "LABEL_PORT": LABEL_PORT, - "LABEL_INGRESS": LABEL_INGRESS, - "LABEL_VOL_CM": LABEL_VOL_CM, - "LABEL_HEALTHCHECK": LABEL_HEALTHCHECK, - "LABEL_SAMEPOD": LABEL_SAMEPOD, - "LABEL_VOLUMEFROM": LABEL_VOLUMEFROM, - "LABEL_EMPTYDIRS": LABEL_EMPTYDIRS, - "LABEL_IGNORE": LABEL_IGNORE, - "LABEL_MAP_ENV": LABEL_MAP_ENV, - "LABEL_SECRETVARS": LABEL_SECRETVARS, - "LABEL_CRON": LABEL_CRON, + "LABEL_ENV_SECRET": LABEL_ENV_SECRET, + "LABEL_PORT": LABEL_PORT, + "LABEL_CONTAINER_PORT": LABEL_CONTAINER_PORT, + "LABEL_INGRESS": LABEL_INGRESS, + "LABEL_VOL_CM": LABEL_VOL_CM, + "LABEL_HEALTHCHECK": LABEL_HEALTHCHECK, + "LABEL_SAMEPOD": LABEL_SAMEPOD, + "LABEL_VOLUMEFROM": LABEL_VOLUMEFROM, + "LABEL_EMPTYDIRS": LABEL_EMPTYDIRS, + "LABEL_IGNORE": LABEL_IGNORE, + "LABEL_MAP_ENV": LABEL_MAP_ENV, + "LABEL_SECRETVARS": LABEL_SECRETVARS, + "LABEL_CRON": LABEL_CRON, }) return buff.String() } -- 2.49.1 From 9bc93044ddefeb74623f5dba94fd8b830d3d9c9e Mon Sep 17 00:00:00 2001 From: Patrice Ferlet Date: Fri, 10 Jun 2022 15:24:26 +0200 Subject: [PATCH 24/26] Make it possible to use multiline script in crons --- generator/crontabs.go | 8 +++++--- go.mod | 1 + go.sum | 2 ++ 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/generator/crontabs.go b/generator/crontabs.go index 6f0a3c1..92b7707 100644 --- a/generator/crontabs.go +++ b/generator/crontabs.go @@ -6,6 +6,7 @@ import ( "katenary/logger" "log" + "github.com/alessio/shellescape" "github.com/compose-spec/compose-go/types" "gopkg.in/yaml.v3" ) @@ -14,11 +15,11 @@ const ( cronMulti = `pods=$(kubectl get pods --selector=%s/component=%s,%s/resource=deployment -o jsonpath='{.items[*].metadata.name}')` cronMultiCmd = ` for pod in $pods; do - kubectl exec -i $pod -c %s -- sh -c '%s' + kubectl exec -i $pod -c %s -- sh -c %s done` cronSingle = `pod=$(kubectl get pods --selector=%s/component=%s,%s/resource=deployment -o jsonpath='{.items[0].metadata.name}')` cronCmd = ` -kubectl exec -i $pod -c %s -- sh -c '%s'` +kubectl exec -i $pod -c %s -- sh -c %s` ) type CronDef struct { @@ -65,6 +66,7 @@ func buildCrontab(deployName string, deployment *helm.Deployment, s *types.Servi // create crontabs for _, cron := range crons { + escaped := shellescape.Quote(cron.Command) var cmd, podget string if cron.Multi { podget = cronMulti @@ -74,7 +76,7 @@ func buildCrontab(deployName string, deployment *helm.Deployment, s *types.Servi cmd = cronCmd } podget = fmt.Sprintf(podget, helm.K, deployName, helm.K) - cmd = fmt.Sprintf(cmd, s.Name, cron.Command) + cmd = fmt.Sprintf(cmd, s.Name, escaped) cmd = podget + cmd if cron.Image == "" { diff --git a/go.mod b/go.mod index 0d140ea..9301a9b 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module katenary go 1.16 require ( + github.com/alessio/shellescape v1.4.1 github.com/compose-spec/compose-go v1.2.5 github.com/distribution/distribution/v3 v3.0.0-20220505155552-985711c1f414 // indirect github.com/kr/pretty v0.2.0 // indirect diff --git a/go.sum b/go.sum index ca5953c..f4c0e19 100644 --- a/go.sum +++ b/go.sum @@ -16,6 +16,8 @@ github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAE github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVKJUX0= +github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/aws/aws-sdk-go v1.34.9/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= github.com/aws/aws-sdk-go v1.43.16/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= -- 2.49.1 From 50e905591b7d9227e17d8c0122be685636ec989a Mon Sep 17 00:00:00 2001 From: Patrice Ferlet Date: Fri, 10 Jun 2022 15:29:00 +0200 Subject: [PATCH 25/26] Fix index and suffix of cronjob names --- generator/crontabs.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/generator/crontabs.go b/generator/crontabs.go index 92b7707..efacb79 100644 --- a/generator/crontabs.go +++ b/generator/crontabs.go @@ -62,7 +62,8 @@ func buildCrontab(deployName string, deployment *helm.Deployment, s *types.Servi fileGeneratorChan <- role fileGeneratorChan <- roleBinding - index := len(crons) - 1 // will be 0 when there is only one cron - made to name crons + numcron := len(crons) - 1 + index := 1 // create crontabs for _, cron := range crons { @@ -84,14 +85,13 @@ func buildCrontab(deployName string, deployment *helm.Deployment, s *types.Servi } name := deployName - if index > 0 { + if numcron > 0 { name = fmt.Sprintf("%s-%d", deployName, index) - index++ } // add crontab suffix := "" - if index > 0 { + if numcron > 0 { suffix = fmt.Sprintf("%d", index) } cronTab := helm.NewCrontab( @@ -103,6 +103,7 @@ func buildCrontab(deployName string, deployment *helm.Deployment, s *types.Servi ) logger.Magenta(ICON_CRON, "Generating crontab", deployName, suffix) fileGeneratorChan <- cronTab + index++ } return -- 2.49.1 From 232e17dfe0c4fd34d744322bb53a2f52e9aa7fe5 Mon Sep 17 00:00:00 2001 From: Patrice Ferlet Date: Fri, 10 Jun 2022 15:59:18 +0200 Subject: [PATCH 26/26] Skip init containers for same pods --- generator/writer.go | 1 + 1 file changed, 1 insertion(+) diff --git a/generator/writer.go b/generator/writer.go index 7542c94..d7de1ce 100644 --- a/generator/writer.go +++ b/generator/writer.go @@ -129,6 +129,7 @@ func Generate(p *compose.Parser, katernayVersion, appName, appVersion, chartVers n := service.Name if linkname, ok := service.Labels[helm.LABEL_SAMEPOD]; ok && linkname == name { linked[n] = service + delete(s.DependsOn, n) } } -- 2.49.1