Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions examples/app/templates/hpa.yaml
Original file line number Diff line number Diff line change
@@ -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: myapp
minReplicas: {{ .Values.hpa.minReplicas }}
maxReplicas: {{ .Values.hpa.maxReplicas }}
targetCPUUtilizationPercentage: {{ .Values.hpa.targetCPUUtilizationPercentage }}
4 changes: 4 additions & 0 deletions examples/app/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ fluentdElasticsearch:
requests:
cpu: 100m
memory: 200Mi
hpa:
maxReplicas: 10
minReplicas: 2
targetCPUUtilizationPercentage: 80
kubernetesClusterDomain: cluster.local
myConfig:
dummyconfigmapkey: dummyconfigmapvalue
Expand Down
14 changes: 14 additions & 0 deletions examples/operator/templates/app-hpa.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
name: {{ include "operator.fullname" . }}-app-hpa
labels:
{{- include "operator.labels" . | nindent 4 }}
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: myapp
minReplicas: {{ .Values.appHpa.minReplicas }}
maxReplicas: {{ .Values.appHpa.maxReplicas }}
targetCPUUtilizationPercentage: {{ .Values.appHpa.targetCPUUtilizationPercentage }}
4 changes: 4 additions & 0 deletions examples/operator/values.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
appHpa:
maxReplicas: 10
minReplicas: 2
targetCPUUtilizationPercentage: 80
configmapVars:
var4: value for var4
controllerManager:
Expand Down
2 changes: 2 additions & 0 deletions pkg/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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) {
Expand Down
117 changes: 117 additions & 0 deletions pkg/processor/horizontalpodautoscaler/hpa.go
Original file line number Diff line number Diff line change
@@ -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
}
42 changes: 42 additions & 0 deletions pkg/processor/horizontalpodautoscaler/hpa_test.go
Original file line number Diff line number Diff line change
@@ -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)
})
}
13 changes: 13 additions & 0 deletions test_data/k8s-operator-kustomize.output
Original file line number Diff line number Diff line change
Expand Up @@ -846,3 +846,16 @@ webhooks:
resources:
- myclusters
sideEffects: None
---
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
name: myapp-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: myapp
minReplicas: 2
maxReplicas: 10
targetCPUUtilizationPercentage: 80
13 changes: 13 additions & 0 deletions test_data/sample-app.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -375,3 +375,16 @@ spec:
selector:
matchLabels:
app: nginx
---
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
name: myapp-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: myapp
minReplicas: 2
maxReplicas: 10
targetCPUUtilizationPercentage: 80
Loading