diff --git a/examples/app/templates/myapp-ingress.yaml b/examples/app/templates/app-ingress.yaml similarity index 74% rename from examples/app/templates/myapp-ingress.yaml rename to examples/app/templates/app-ingress.yaml index 826912d3..8e0a0ca3 100644 --- a/examples/app/templates/myapp-ingress.yaml +++ b/examples/app/templates/app-ingress.yaml @@ -1,7 +1,7 @@ apiVersion: networking.k8s.io/v1 kind: Ingress metadata: - name: {{ include "app.fullname" . }}-myapp-ingress + name: {{ include "app.fullname" . }}-app-ingress labels: {{- include "app.labels" . | nindent 4 }} annotations: @@ -12,7 +12,7 @@ spec: paths: - backend: service: - name: '{{ include "app.fullname" . }}-myapp-service' + name: '{{ include "app.fullname" . }}-app-service' port: number: 8443 path: /testpath diff --git a/examples/app/templates/app-lb-service.yaml b/examples/app/templates/app-lb-service.yaml new file mode 100644 index 00000000..3bd8443a --- /dev/null +++ b/examples/app/templates/app-lb-service.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "app.fullname" . }}-app-lb-service + labels: + app: myapp + {{- include "app.labels" . | nindent 4 }} +spec: + type: {{ .Values.appLbService.type }} + selector: + app: myapp + {{- include "app.selectorLabels" . | nindent 4 }} + ports: + {{- .Values.appLbService.ports | toYaml | nindent 2 }} + loadBalancerSourceRanges: + {{- .Values.appLbService.loadBalancerSourceRanges | toYaml | nindent 2 }} diff --git a/examples/app/templates/myapp-pdb.yaml b/examples/app/templates/app-pdb.yaml similarity index 59% rename from examples/app/templates/myapp-pdb.yaml rename to examples/app/templates/app-pdb.yaml index cc17f21b..fa5f2d78 100644 --- a/examples/app/templates/myapp-pdb.yaml +++ b/examples/app/templates/app-pdb.yaml @@ -1,13 +1,13 @@ apiVersion: policy/v1 kind: PodDisruptionBudget metadata: - name: {{ include "app.fullname" . }}-myapp-pdb + name: {{ include "app.fullname" . }}-app-pdb labels: app: nginx {{- include "app.labels" . | nindent 4 }} spec: - minAvailable: {{ .Values.myappPdb.minAvailable }} - maxUnavailable: {{ .Values.myappPdb.maxUnavailable }} + minAvailable: {{ .Values.appPdb.minAvailable }} + maxUnavailable: {{ .Values.appPdb.maxUnavailable }} selector: matchLabels: app: nginx diff --git a/examples/app/templates/myapp-service.yaml b/examples/app/templates/app-service.yaml similarity index 57% rename from examples/app/templates/myapp-service.yaml rename to examples/app/templates/app-service.yaml index 1370ab26..286d05d5 100644 --- a/examples/app/templates/myapp-service.yaml +++ b/examples/app/templates/app-service.yaml @@ -1,14 +1,14 @@ apiVersion: v1 kind: Service metadata: - name: {{ include "app.fullname" . }}-myapp-service + name: {{ include "app.fullname" . }}-app-service labels: app: myapp {{- include "app.labels" . | nindent 4 }} spec: - type: {{ .Values.myappService.type }} + type: {{ .Values.appService.type }} selector: app: myapp {{- include "app.selectorLabels" . | nindent 4 }} ports: - {{- .Values.myappService.ports | toYaml | nindent 2 }} + {{- .Values.appService.ports | toYaml | nindent 2 }} diff --git a/examples/app/templates/config-props.yaml b/examples/app/templates/config-props.yaml new file mode 100644 index 00000000..3cd0f4ca --- /dev/null +++ b/examples/app/templates/config-props.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "app.fullname" . }}-config-props + labels: + {{- include "app.labels" . | nindent 4 }} +data: + my.prop1: {{ .Values.configProps.myProp1 | quote }} + my.prop2: {{ .Values.configProps.myProp2 | quote }} + my.prop3: {{ .Values.configProps.myProp3 | quote }} + myval.yaml: {{ .Values.configProps.myvalYaml | toYaml | indent 1 }} diff --git a/examples/app/templates/config.yaml b/examples/app/templates/config.yaml new file mode 100644 index 00000000..cc4023c6 --- /dev/null +++ b/examples/app/templates/config.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "app.fullname" . }}-config + labels: + {{- include "app.labels" . | nindent 4 }} +immutable: true +data: + dummyconfigmapkey: {{ .Values.config.dummyconfigmapkey | quote }} + my_config.properties: | + health.healthProbeBindAddress={{ .Values.config.myConfigProperties.health.healthProbeBindAddress | quote }} + metrics.bindAddress={{ .Values.config.myConfigProperties.metrics.bindAddress | quote }} diff --git a/examples/app/templates/deployment.yaml b/examples/app/templates/deployment.yaml index 4d691eb3..3f6c7bd8 100644 --- a/examples/app/templates/deployment.yaml +++ b/examples/app/templates/deployment.yaml @@ -1,13 +1,13 @@ apiVersion: apps/v1 kind: Deployment metadata: - name: {{ include "app.fullname" . }}-myapp + name: {{ include "app.fullname" . }}-app labels: app: myapp {{- include "app.labels" . | nindent 4 }} spec: - replicas: {{ .Values.myapp.replicas }} - revisionHistoryLimit: {{ .Values.myapp.revisionHistoryLimit }} + replicas: {{ .Values.app.replicas }} + revisionHistoryLimit: {{ .Values.app.revisionHistoryLimit }} selector: matchLabels: app: myapp @@ -19,7 +19,7 @@ spec: {{- include "app.selectorLabels" . | nindent 8 }} spec: containers: - - args: {{- toYaml .Values.myapp.app.args | nindent 8 }} + - args: {{- toYaml .Values.app.app.args | nindent 8 }} command: - /manager env: @@ -27,12 +27,12 @@ spec: valueFrom: secretKeyRef: key: VAR1 - name: {{ include "app.fullname" . }}-my-secret-vars + name: {{ include "app.fullname" . }}-secret-vars - name: VAR2 valueFrom: secretKeyRef: key: VAR2 - name: {{ include "app.fullname" . }}-my-secret-vars + name: {{ include "app.fullname" . }}-secret-vars - name: APP_NAME valueFrom: fieldRef: @@ -43,8 +43,8 @@ spec: fieldPath: metadata.labels['app.kubernetes.io/instance'] - name: KUBERNETES_CLUSTER_DOMAIN value: {{ quote .Values.kubernetesClusterDomain }} - image: {{ .Values.myapp.app.image.repository }}:{{ .Values.myapp.app.image.tag - | default .Chart.AppVersion }} + image: {{ .Values.app.app.image.repository }}:{{ .Values.app.app.image.tag | default + .Chart.AppVersion }} livenessProbe: httpGet: path: /healthz @@ -58,8 +58,8 @@ spec: port: 8081 initialDelaySeconds: 5 periodSeconds: 10 - resources: {{- toYaml .Values.myapp.app.resources | nindent 10 }} - securityContext: {{- toYaml .Values.myapp.app.containerSecurityContext | nindent + resources: {{- toYaml .Values.app.app.resources | nindent 10 }} + securityContext: {{- toYaml .Values.app.app.containerSecurityContext | nindent 10 }} volumeMounts: - mountPath: /my_config.properties @@ -71,11 +71,11 @@ spec: name: props - mountPath: /usr/share/nginx/html name: sample-pv-storage - - args: {{- toYaml .Values.myapp.proxySidecar.args | nindent 8 }} + - args: {{- toYaml .Values.app.proxySidecar.args | nindent 8 }} env: - name: KUBERNETES_CLUSTER_DOMAIN value: {{ quote .Values.kubernetesClusterDomain }} - image: {{ .Values.myapp.proxySidecar.image.repository }}:{{ .Values.myapp.proxySidecar.image.tag + image: {{ .Values.app.proxySidecar.image.repository }}:{{ .Values.app.proxySidecar.image.tag | default .Chart.AppVersion }} name: proxy-sidecar ports: @@ -90,23 +90,23 @@ spec: env: - name: KUBERNETES_CLUSTER_DOMAIN value: {{ quote .Values.kubernetesClusterDomain }} - image: {{ .Values.myapp.initContainer.image.repository }}:{{ .Values.myapp.initContainer.image.tag + image: {{ .Values.app.initContainer.image.repository }}:{{ .Values.app.initContainer.image.tag | default .Chart.AppVersion }} name: init-container resources: {} - nodeSelector: {{- toYaml .Values.myapp.nodeSelector | nindent 8 }} - securityContext: {{- toYaml .Values.myapp.podSecurityContext | nindent 8 }} + nodeSelector: {{- toYaml .Values.app.nodeSelector | nindent 8 }} + securityContext: {{- toYaml .Values.app.podSecurityContext | nindent 8 }} terminationGracePeriodSeconds: 10 volumes: - configMap: - name: {{ include "app.fullname" . }}-my-config + name: {{ include "app.fullname" . }}-config name: manager-config - configMap: - name: {{ include "app.fullname" . }}-my-config-props + name: {{ include "app.fullname" . }}-config-props name: props - name: secret-volume secret: - secretName: {{ include "app.fullname" . }}-my-secret-ca + secretName: {{ include "app.fullname" . }}-secret-ca - name: sample-pv-storage persistentVolumeClaim: - claimName: {{ include "app.fullname" . }}-my-sample-pv-claim + claimName: {{ include "app.fullname" . }}-sample-pv-claim diff --git a/examples/app/templates/hpa.yaml b/examples/app/templates/hpa.yaml new file mode 100644 index 00000000..4992b108 --- /dev/null +++ b/examples/app/templates/hpa.yaml @@ -0,0 +1,14 @@ +apiVersion: autoscaling/v1 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "app.fullname" . }}-hpa + labels: + {{- include "app.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: mydep + minReplicas: {{ .Values.hpa.minReplicas }} + maxReplicas: {{ .Values.hpa.maxReplicas }} + targetCPUUtilizationPercentage: {{ .Values.hpa.targetCPUUtilizationPercentage }} diff --git a/examples/app/templates/my-config-props.yaml b/examples/app/templates/my-config-props.yaml deleted file mode 100644 index af3564c4..00000000 --- a/examples/app/templates/my-config-props.yaml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ include "app.fullname" . }}-my-config-props - labels: - {{- include "app.labels" . | nindent 4 }} -data: - my.prop1: {{ .Values.myConfigProps.myProp1 | quote }} - my.prop2: {{ .Values.myConfigProps.myProp2 | quote }} - my.prop3: {{ .Values.myConfigProps.myProp3 | quote }} - myval.yaml: {{ .Values.myConfigProps.myvalYaml | toYaml | indent 1 }} diff --git a/examples/app/templates/my-config.yaml b/examples/app/templates/my-config.yaml deleted file mode 100644 index 8fcb2257..00000000 --- a/examples/app/templates/my-config.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ include "app.fullname" . }}-my-config - labels: - {{- include "app.labels" . | nindent 4 }} -immutable: true -data: - dummyconfigmapkey: {{ .Values.myConfig.dummyconfigmapkey | quote }} - my_config.properties: | - health.healthProbeBindAddress={{ .Values.myConfig.myConfigProperties.health.healthProbeBindAddress | quote }} - metrics.bindAddress={{ .Values.myConfig.myConfigProperties.metrics.bindAddress | quote }} diff --git a/examples/app/templates/my-sample-pv-claim.yaml b/examples/app/templates/my-sample-pv-claim.yaml deleted file mode 100644 index 4f52287f..00000000 --- a/examples/app/templates/my-sample-pv-claim.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: {{ include "app.fullname" . }}-my-sample-pv-claim - labels: - {{- include "app.labels" . | nindent 4 }} -spec: - accessModes: - - ReadWriteOnce - resources: - limits: - storage: {{ .Values.pvc.mySamplePvClaim.storageLimit | quote }} - requests: - storage: {{ .Values.pvc.mySamplePvClaim.storageRequest | quote }} - storageClassName: {{ .Values.pvc.mySamplePvClaim.storageClass | quote }} diff --git a/examples/app/templates/my-secret-ca.yaml b/examples/app/templates/my-secret-ca.yaml deleted file mode 100644 index 285c5e57..00000000 --- a/examples/app/templates/my-secret-ca.yaml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: v1 -kind: Secret -metadata: - name: {{ include "app.fullname" . }}-my-secret-ca - labels: - {{- include "app.labels" . | nindent 4 }} -data: - ca.crt: {{ required "mySecretCa.caCrt is required" .Values.mySecretCa.caCrt | b64enc - | quote }} -type: opaque diff --git a/examples/app/templates/my-secret-vars.yaml b/examples/app/templates/my-secret-vars.yaml deleted file mode 100644 index df350e51..00000000 --- a/examples/app/templates/my-secret-vars.yaml +++ /dev/null @@ -1,17 +0,0 @@ -apiVersion: v1 -kind: Secret -metadata: - name: {{ include "app.fullname" . }}-my-secret-vars - labels: - {{- include "app.labels" . | nindent 4 }} -data: - ELASTIC_FOOBAR_HUNTER123_MEOWTOWN_VERIFY: {{ required "mySecretVars.elasticFoobarHunter123MeowtownVerify is required" .Values.mySecretVars.elasticFoobarHunter123MeowtownVerify | b64enc - | quote }} - VAR1: {{ required "mySecretVars.var1 is required" .Values.mySecretVars.var1 | b64enc - | quote }} - VAR2: {{ required "mySecretVars.var2 is required" .Values.mySecretVars.var2 | b64enc - | quote }} -stringData: - str: {{ required "mySecretVars.str is required" .Values.mySecretVars.str | quote - }} -type: opaque diff --git a/examples/app/templates/myapp-lb-service.yaml b/examples/app/templates/myapp-lb-service.yaml deleted file mode 100644 index 5a8b9bbb..00000000 --- a/examples/app/templates/myapp-lb-service.yaml +++ /dev/null @@ -1,16 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: {{ include "app.fullname" . }}-myapp-lb-service - labels: - app: myapp - {{- include "app.labels" . | nindent 4 }} -spec: - type: {{ .Values.myappLbService.type }} - selector: - app: myapp - {{- include "app.selectorLabels" . | nindent 4 }} - ports: - {{- .Values.myappLbService.ports | toYaml | nindent 2 }} - loadBalancerSourceRanges: - {{- .Values.myappLbService.loadBalancerSourceRanges | toYaml | nindent 2 }} diff --git a/examples/app/templates/sample-pv-claim.yaml b/examples/app/templates/sample-pv-claim.yaml new file mode 100644 index 00000000..36f47e15 --- /dev/null +++ b/examples/app/templates/sample-pv-claim.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "app.fullname" . }}-sample-pv-claim + labels: + {{- include "app.labels" . | nindent 4 }} +spec: + accessModes: + - ReadWriteOnce + resources: + limits: + storage: {{ .Values.pvc.samplePvClaim.storageLimit | quote }} + requests: + storage: {{ .Values.pvc.samplePvClaim.storageRequest | quote }} + storageClassName: {{ .Values.pvc.samplePvClaim.storageClass | quote }} diff --git a/examples/app/templates/secret-ca.yaml b/examples/app/templates/secret-ca.yaml new file mode 100644 index 00000000..8e3812f0 --- /dev/null +++ b/examples/app/templates/secret-ca.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "app.fullname" . }}-secret-ca + labels: + {{- include "app.labels" . | nindent 4 }} +data: + ca.crt: {{ required "secretCa.caCrt is required" .Values.secretCa.caCrt | b64enc + | quote }} +type: opaque diff --git a/examples/app/templates/secret-vars.yaml b/examples/app/templates/secret-vars.yaml new file mode 100644 index 00000000..7b939b64 --- /dev/null +++ b/examples/app/templates/secret-vars.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "app.fullname" . }}-secret-vars + labels: + {{- include "app.labels" . | nindent 4 }} +data: + ELASTIC_FOOBAR_HUNTER123_MEOWTOWN_VERIFY: {{ required "secretVars.elasticFoobarHunter123MeowtownVerify is required" .Values.secretVars.elasticFoobarHunter123MeowtownVerify | b64enc + | quote }} + VAR1: {{ required "secretVars.var1 is required" .Values.secretVars.var1 | b64enc + | quote }} + VAR2: {{ required "secretVars.var2 is required" .Values.secretVars.var2 | b64enc + | quote }} +stringData: + str: {{ required "secretVars.str is required" .Values.secretVars.str | quote }} +type: opaque diff --git a/examples/app/values.yaml b/examples/app/values.yaml index 2d23d620..853ae5ad 100644 --- a/examples/app/values.yaml +++ b/examples/app/values.yaml @@ -1,57 +1,4 @@ -batchJob: - backoffLimit: 4 - pi: - image: - repository: perl - tag: 5.34.0 -cronJob: - hello: - image: - repository: busybox - tag: "1.28" - imagePullPolicy: IfNotPresent - schedule: '* * * * *' -fluentdElasticsearch: - fluentdElasticsearch: - image: - repository: quay.io/fluentd_elasticsearch/fluentd - tag: v2.5.2 - resources: - limits: - memory: 200Mi - requests: - cpu: 100m - memory: 200Mi -kubernetesClusterDomain: cluster.local -myConfig: - dummyconfigmapkey: dummyconfigmapvalue - myConfigProperties: - health: - healthProbeBindAddress: "8081" - metrics: - bindAddress: 127.0.0.1:8080 -myConfigProps: - myProp1: "1" - myProp2: val 1 - myProp3: "true" - myvalYaml: |- - apiVersion: clickhouse.altinity.com/v1 - kind: ClickHouseInstallationTemplate - metadata: - name: default-oneperhost-pod-template - spec: - templates: - podTemplates: - - name: default-oneperhost-pod-template - distribution: "OnePerHost" -mySecretCa: - caCrt: "" -mySecretVars: - elasticFoobarHunter123MeowtownVerify: "" - str: "" - var1: "" - var2: "" -myapp: +app: app: args: - --health-probe-bind-address=:8081 @@ -89,7 +36,7 @@ myapp: tag: v0.8.0 replicas: 3 revisionHistoryLimit: 5 -myappLbService: +appLbService: loadBalancerSourceRanges: - 10.0.0.0/8 ports: @@ -97,14 +44,64 @@ myappLbService: port: 8443 targetPort: https type: LoadBalancer -myappPdb: +appPdb: minAvailable: 2 -myappService: +appService: ports: - name: https port: 8443 targetPort: https type: ClusterIP +batchJob: + backoffLimit: 4 + pi: + image: + repository: perl + tag: 5.34.0 +config: + dummyconfigmapkey: dummyconfigmapvalue + myConfigProperties: + health: + healthProbeBindAddress: "8081" + metrics: + bindAddress: 127.0.0.1:8080 +configProps: + myProp1: "1" + myProp2: val 1 + myProp3: "true" + myvalYaml: |- + apiVersion: clickhouse.altinity.com/v1 + kind: ClickHouseInstallationTemplate + metadata: + name: default-oneperhost-pod-template + spec: + templates: + podTemplates: + - name: default-oneperhost-pod-template + distribution: "OnePerHost" +cronJob: + hello: + image: + repository: busybox + tag: "1.28" + imagePullPolicy: IfNotPresent + schedule: '* * * * *' +fluentdElasticsearch: + fluentdElasticsearch: + image: + repository: quay.io/fluentd_elasticsearch/fluentd + tag: v2.5.2 + resources: + limits: + memory: 200Mi + requests: + cpu: 100m + memory: 200Mi +hpa: + maxReplicas: 10 + minReplicas: 2 + targetCPUUtilizationPercentage: 80 +kubernetesClusterDomain: cluster.local nginx: ports: - name: web @@ -112,10 +109,17 @@ nginx: targetPort: 0 type: ClusterIP pvc: - mySamplePvClaim: + samplePvClaim: storageClass: manual storageLimit: 5Gi storageRequest: 3Gi +secretCa: + caCrt: "" +secretVars: + elasticFoobarHunter123MeowtownVerify: "" + str: "" + var1: "" + var2: "" web: nginx: image: diff --git a/pkg/app/app.go b/pkg/app/app.go index ce0fe8f9..3456586d 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -8,6 +8,7 @@ import ( "syscall" "github.com/arttor/helmify/pkg/file" + "github.com/arttor/helmify/pkg/processor/horizontalpodautoscaler" "github.com/arttor/helmify/pkg/processor/job" "github.com/arttor/helmify/pkg/processor/poddisruptionbudget" "github.com/arttor/helmify/pkg/processor/statefulset" @@ -67,6 +68,7 @@ func Start(stdin io.Reader, config config.Config) error { job.NewCron(), job.NewJob(), poddisruptionbudget.New(), + horizontalpodautoscaler.New(), ).WithDefaultProcessor(processor.Default()) if len(config.Files) != 0 { file.Walk(config.Files, config.FilesRecursively, func(filename string, fileReader io.Reader) { diff --git a/pkg/processor/horizontalpodautoscaler/hpa.go b/pkg/processor/horizontalpodautoscaler/hpa.go new file mode 100644 index 00000000..21673496 --- /dev/null +++ b/pkg/processor/horizontalpodautoscaler/hpa.go @@ -0,0 +1,117 @@ +package horizontalpodautoscaler + +import ( + "bytes" + "fmt" + "io" + + "github.com/arttor/helmify/pkg/processor" + + "github.com/arttor/helmify/pkg/helmify" + yamlformat "github.com/arttor/helmify/pkg/yaml" + "github.com/iancoleman/strcase" + autoscalingv1 "k8s.io/api/autoscaling/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/yaml" +) + +const ( + hpaTempSpec = ` +spec: + scaleTargetRef: +%[1]s + minReplicas: {{ .Values.%[2]s.minReplicas }} + maxReplicas: {{ .Values.%[2]s.maxReplicas }} + targetCPUUtilizationPercentage: {{ .Values.%[2]s.targetCPUUtilizationPercentage }}` +) + +var hpaGVC = schema.GroupVersionKind{ + Group: "autoscaling", + Version: "v1", + Kind: "HorizontalPodAutoscaler", +} + +// New creates processor for k8s Service resource. +func New() helmify.Processor { + return &hpa{} +} + +type hpa struct{} + +// Process k8s Service object into template. Returns false if not capable of processing given resource type. +func (r hpa) Process(appMeta helmify.AppMetadata, obj *unstructured.Unstructured) (bool, helmify.Template, error) { + if obj.GroupVersionKind() != hpaGVC { + return false, nil, nil + } + hpa := autoscalingv1.HorizontalPodAutoscaler{} + err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, &hpa) + if err != nil { + return true, nil, fmt.Errorf("%w: unable to cast to hpa", err) + } + spec := hpa.Spec + values := helmify.Values{} + + meta, err := processor.ProcessObjMeta(appMeta, obj) + if err != nil { + return true, nil, err + } + + name := appMeta.TrimName(obj.GetName()) + nameCamel := strcase.ToLowerCamel(name) + + scaleTargetRef, err := yaml.Marshal(hpa.Spec.ScaleTargetRef) + if err != nil { + return true, nil, fmt.Errorf("error marshaling scaleTargetRef: %w", err) + } + scaleTargetRef = yamlformat.Indent(scaleTargetRef, 4) + scaleTargetRef = bytes.TrimRight(scaleTargetRef, "\n ") + + if spec.MinReplicas != nil && *spec.MinReplicas != 0 { + _, err := values.Add(*spec.MinReplicas, nameCamel, "minReplicas") + if err != nil { + return true, nil, err + } + } + + if spec.MaxReplicas != 0 { + _, err := values.Add(spec.MaxReplicas, nameCamel, "maxReplicas") + if err != nil { + return true, nil, err + } + } + + if spec.TargetCPUUtilizationPercentage != nil && *spec.TargetCPUUtilizationPercentage != 0 { + _, err := values.Add(*spec.TargetCPUUtilizationPercentage, nameCamel, "targetCPUUtilizationPercentage") + if err != nil { + return true, nil, err + } + } + + res := meta + fmt.Sprintf(hpaTempSpec, scaleTargetRef, nameCamel) + return true, &result{ + name: name, + data: res, + values: values, + }, nil +} + +type result struct { + name string + data string + values helmify.Values +} + +func (r *result) Filename() string { + return r.name + ".yaml" +} + +func (r *result) Values() helmify.Values { + return r.values +} + +func (r *result) Write(writer io.Writer) error { + _, err := writer.Write([]byte(r.data)) + return err +} diff --git a/pkg/processor/horizontalpodautoscaler/hpa_test.go b/pkg/processor/horizontalpodautoscaler/hpa_test.go new file mode 100644 index 00000000..5de6c423 --- /dev/null +++ b/pkg/processor/horizontalpodautoscaler/hpa_test.go @@ -0,0 +1,42 @@ +package horizontalpodautoscaler + +import ( + "os" + "testing" + + "github.com/arttor/helmify/pkg/metadata" + + "github.com/arttor/helmify/internal" + "github.com/stretchr/testify/assert" +) + +const hpaYaml = `apiVersion: autoscaling/v1 +kind: HorizontalPodAutoscaler +metadata: + name: myapp-hpa +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: myapp + minReplicas: 2 + maxReplicas: 10 + targetCPUUtilizationPercentage: 80` + +func Test_hpa_Process(t *testing.T) { + var testInstance hpa + + t.Run("processed", func(t *testing.T) { + obj := internal.GenerateObj(hpaYaml) + processed, tt, err := testInstance.Process(&metadata.Service{}, obj) + _ = tt.Write(os.Stdout) + assert.NoError(t, err) + assert.Equal(t, true, processed) + }) + t.Run("skipped", func(t *testing.T) { + obj := internal.TestNs + processed, _, err := testInstance.Process(&metadata.Service{}, obj) + assert.NoError(t, err) + assert.Equal(t, false, processed) + }) +} diff --git a/test_data/sample-app.yaml b/test_data/sample-app.yaml index d2399c81..f510d3f7 100644 --- a/test_data/sample-app.yaml +++ b/test_data/sample-app.yaml @@ -375,3 +375,16 @@ spec: selector: matchLabels: app: nginx +--- +apiVersion: autoscaling/v1 +kind: HorizontalPodAutoscaler +metadata: + name: my-hpa +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: mydep + minReplicas: 2 + maxReplicas: 10 + targetCPUUtilizationPercentage: 80 \ No newline at end of file