Skip to content

Commit

Permalink
feature: add support to keda scaledobject v1alpha1 (#711)
Browse files Browse the repository at this point in the history
Co-authored-by: Jonathan Henrique Medeiros <[email protected]>
Co-authored-by: Tomasz Janiszewski <[email protected]>
  • Loading branch information
3 people authored Feb 22, 2024
1 parent cd1e926 commit 39d132c
Show file tree
Hide file tree
Showing 10 changed files with 440 additions and 221 deletions.
4 changes: 3 additions & 1 deletion e2etests/bats-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -639,11 +639,13 @@ get_value_from() {

message1=$(get_value_from "${lines[0]}" '.Reports[0].Object.K8sObject.GroupVersionKind.Kind + ": " + .Reports[0].Diagnostic.Message')
message2=$(get_value_from "${lines[0]}" '.Reports[1].Object.K8sObject.GroupVersionKind.Kind + ": " + .Reports[1].Diagnostic.Message')
message3=$(get_value_from "${lines[0]}" '.Reports[2].Object.K8sObject.GroupVersionKind.Kind + ": " + .Reports[2].Diagnostic.Message')
count=$(get_value_from "${lines[0]}" '.Reports | length')

[[ "${message1}" == "PodDisruptionBudget: The current number of replicas for deployment foo is equal to or lower than the minimum number of replicas specified by its PDB." ]]
[[ "${message2}" == "PodDisruptionBudget: The current number of replicas for deployment foo2 is equal to or lower than the minimum number of replicas specified by its PDB." ]]
[[ "${count}" == "2" ]]
[[ "${message3}" == "PodDisruptionBudget: The current number of replicas for deployment foo3 is equal to or lower than the minimum number of replicas specified by its PDB." ]]
[[ "${count}" == "3" ]]
}

@test "privilege-escalation-container" {
Expand Down
138 changes: 73 additions & 65 deletions go.mod

Large diffs are not rendered by default.

312 changes: 164 additions & 148 deletions go.sum

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions pkg/extract/hpa_spec.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package extract

import (
kedaV1Alpha1 "github.com/kedacore/keda/v2/apis/keda/v1alpha1"
"golang.stackrox.io/kube-linter/pkg/k8sutil"
autoscalingV1 "k8s.io/api/autoscaling/v1"
autoscalingV2 "k8s.io/api/autoscaling/v2"
Expand All @@ -19,6 +20,8 @@ func HPAMinReplicas(obj k8sutil.Object) (int32, bool) {
return checkReplicas(hpa.Spec.MinReplicas)
case *autoscalingV1.HorizontalPodAutoscaler:
return checkReplicas(hpa.Spec.MinReplicas)
case *kedaV1Alpha1.ScaledObject:
return checkReplicas(hpa.Spec.MinReplicaCount)
default:
return 0, false
}
Expand All @@ -43,6 +46,8 @@ func HPAScaleTargetRefName(obj k8sutil.Object) (string, bool) {
return hpa.Spec.ScaleTargetRef.Name, true
case *autoscalingV1.HorizontalPodAutoscaler:
return hpa.Spec.ScaleTargetRef.Name, true
case *kedaV1Alpha1.ScaledObject:
return hpa.Spec.ScaleTargetRef.Name, true
default:
return "", false
}
Expand Down
37 changes: 37 additions & 0 deletions pkg/lintcontext/mocks/scaledobject.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package mocks

import (
"fmt"
"testing"

kedaV1Alpha1 "github.com/kedacore/keda/v2/apis/keda/v1alpha1"
"github.com/stretchr/testify/require"
"golang.stackrox.io/kube-linter/pkg/objectkinds"

metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// AddMockScaledObject adds a mock ScaledObject to LintContext
func (l *MockLintContext) AddMockScaledObject(t *testing.T, name, version string) {
require.NotEmpty(t, name)
switch version {
case "v1alpha1":
l.objects[name] = &kedaV1Alpha1.ScaledObject{
TypeMeta: metaV1.TypeMeta{
Kind: objectkinds.ScaledObject,
APIVersion: objectkinds.GetScaledObjectAPIVersion(version),
},
ObjectMeta: metaV1.ObjectMeta{Name: name},
Spec: kedaV1Alpha1.ScaledObjectSpec{},
}
default:
require.FailNow(t, fmt.Sprintf("Unknown scaled object version %s", version))
}
}

// ModifyScaledObjectV1Alpha1 modifies a given ScaledObject in the context via the passed function.
func (l *MockLintContext) ModifyScaledObjectV1Alpha1(t *testing.T, name string, f func(hpa *kedaV1Alpha1.ScaledObject)) {
r, ok := l.objects[name].(*kedaV1Alpha1.ScaledObject)
require.True(t, ok)
f(r)
}
3 changes: 2 additions & 1 deletion pkg/lintcontext/parse_yaml.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (

"github.com/bmatcuk/doublestar/v4"
y "github.com/ghodss/yaml"
kedaV1Alpha1 "github.com/kedacore/keda/v2/apis/keda/v1alpha1"
ocsAppsV1 "github.com/openshift/api/apps/v1"
ocpSecV1 "github.com/openshift/api/security/v1"
"github.com/pkg/errors"
Expand Down Expand Up @@ -44,7 +45,7 @@ func init() {
clientScheme := scheme.Scheme

// Add OpenShift and Autoscaling schema
schemeBuilder := runtime.NewSchemeBuilder(ocsAppsV1.AddToScheme, autoscalingV2Beta1.AddToScheme, k8sMonitoring.AddToScheme, ocpSecV1.AddToScheme)
schemeBuilder := runtime.NewSchemeBuilder(ocsAppsV1.AddToScheme, autoscalingV2Beta1.AddToScheme, k8sMonitoring.AddToScheme, ocpSecV1.AddToScheme, kedaV1Alpha1.AddToScheme)
if err := schemeBuilder.AddToScheme(clientScheme); err != nil {
panic(fmt.Sprintf("Can not add OpenShift schema %v", err))
}
Expand Down
30 changes: 30 additions & 0 deletions pkg/objectkinds/scaledobject.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package objectkinds

import (
"fmt"

kedaV1Alpha1 "github.com/kedacore/keda/v2/apis/keda/v1alpha1"
"k8s.io/apimachinery/pkg/runtime/schema"
)

const (
// ScaledObject represents Kubernetes ScaledObject objects. Case sensitive.
ScaledObject = "ScaledObject"
)

var (
ScaledObjectV1Alpha1 = kedaV1Alpha1.SchemeGroupVersion.WithKind(ScaledObject)
)

func isScaledObject(gvk schema.GroupVersionKind) bool {
return gvk == ScaledObjectV1Alpha1
}

func init() {
RegisterObjectKind(ScaledObject, MatcherFunc(isScaledObject))
}

// GetScaledObjectAPIVersion returns ScaledObject's APIVersion
func GetScaledObjectAPIVersion(version string) string {
return fmt.Sprintf("%s/%s", ScaledObjectV1Alpha1.Group, version)
}
11 changes: 6 additions & 5 deletions pkg/templates/pdbminavailable/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,22 +181,23 @@ func getIntOrPercentValueSafelyFromString(intOrStr string) (int, bool, error) {
return v, true, nil
}

// Function to get the list of HPA's provided
// Function to get the list of HPA's/ScaledObject's provided
func getHorizontalPodAutoscalers(lintCtx lintcontext.LintContext, namespace string) map[string]k8sutil.Object {

m := make(map[string]k8sutil.Object, len(lintCtx.Objects()))

for _, obj := range lintCtx.Objects() {
// Ensure that only HPA objects are processed
if obj.GetK8sObjectName().GroupVersionKind.Kind != objectkinds.HorizontalPodAutoscaler {
// Ensure that HPA/ScaledObject objects are processed
kind := obj.GetK8sObjectName().GroupVersionKind.Kind
if kind != objectkinds.HorizontalPodAutoscaler && kind != objectkinds.ScaledObject {
continue
}

// Ensure that only HPAs are in the same namespaces as the PDB
// Ensure that only HPAs/ScaledObject are in the same namespaces as the PDB
if obj.GetK8sObjectName().Namespace != namespace {
continue
}
// validate object with HPA versions using the HPAScaleTargetRefName extractor package function and add to map
// validate object with HPA/ScaledObject versions using the HPAScaleTargetRefName extractor package function and add to map
hpaSpecScaleTargetRefName, ok := extract.HPAScaleTargetRefName(obj.K8sObject)
if !ok {
continue
Expand Down
77 changes: 77 additions & 0 deletions pkg/templates/pdbminavailable/template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package pdbminavailable
import (
"testing"

kedaV1Alpha1 "github.com/kedacore/keda/v2/apis/keda/v1alpha1"
"github.com/stretchr/testify/suite"
"golang.stackrox.io/kube-linter/internal/pointers"
"golang.stackrox.io/kube-linter/pkg/diagnostic"
Expand Down Expand Up @@ -219,3 +220,79 @@ func (p *PDBTestSuite) TestPDBWithMinAvailableHPA() {
})

}

// test that the check run with a deployment that has no replicas and a Keda ScaledObject that don't has a minReplicas
func (p *PDBTestSuite) TestPDBWithMinAvailableAndKedaScaledObjectDoNotHasMinReplicas() {
p.ctx.AddMockDeployment(p.T(), "test-deploy")
p.ctx.ModifyDeployment(p.T(), "test-deploy", func(deployment *appsV1.Deployment) {
deployment.Namespace = "test"
deployment.Spec.Replicas = nil
deployment.Spec.Selector = &metaV1.LabelSelector{}
deployment.Spec.Selector.MatchLabels = map[string]string{"foo": "bar"}
})
p.ctx.AddMockScaledObject(p.T(), "test-scaledobject", "v1alpha1")
p.ctx.ModifyScaledObjectV1Alpha1(p.T(), "test-scaledobject", func(scaledobject *kedaV1Alpha1.ScaledObject) {
scaledobject.Namespace = "test"
scaledobject.Spec.ScaleTargetRef = &kedaV1Alpha1.ScaleTarget{
Kind: "Deployment",
Name: "test-deploy",
APIVersion: "apps/v1",
}
scaledobject.Spec.MinReplicaCount = nil
})
p.ctx.AddMockPodDisruptionBudget(p.T(), "test-pdb")
p.ctx.ModifyPodDisruptionBudget(p.T(), "test-pdb", func(pdb *v1.PodDisruptionBudget) {
pdb.Namespace = "test"
pdb.Spec.Selector = &metaV1.LabelSelector{}
pdb.Spec.Selector.MatchLabels = map[string]string{"foo": "bar"}
pdb.Spec.MinAvailable = &intstr.IntOrString{StrVal: "50%", Type: intstr.String}
})
p.Validate(p.ctx, []templates.TestCase{
{
Param: params.Params{},
Diagnostics: map[string][]diagnostic.Diagnostic{
"test-pdb": {
{Message: "The current number of replicas for deployment test-deploy is equal to or lower than the minimum number of replicas specified by its PDB."},
},
},
ExpectInstantiationError: false,
},
})
}

// test that the check run with a deployment that has no replicas and a Keda ScaledObject that has a minReplicas
func (p *PDBTestSuite) TestPDBWithMinAvailableAndKedaScaledObjectHasMinReplicas() {
p.ctx.AddMockDeployment(p.T(), "test-deploy")
p.ctx.ModifyDeployment(p.T(), "test-deploy", func(deployment *appsV1.Deployment) {
deployment.Namespace = "test"
deployment.Spec.Replicas = nil
deployment.Spec.Selector = &metaV1.LabelSelector{}
deployment.Spec.Selector.MatchLabels = map[string]string{"foo": "bar"}
})
p.ctx.AddMockScaledObject(p.T(), "test-scaledobject", "v1alpha1")
p.ctx.ModifyScaledObjectV1Alpha1(p.T(), "test-scaledobject", func(scaledobject *kedaV1Alpha1.ScaledObject) {
scaledobject.Namespace = "test"
scaledobject.Spec.ScaleTargetRef = &kedaV1Alpha1.ScaleTarget{
Kind: "Deployment",
Name: "test-deploy",
APIVersion: "apps/v1",
}
scaledobject.Spec.MinReplicaCount = pointers.Int32(4)
})
p.ctx.AddMockPodDisruptionBudget(p.T(), "test-pdb")
p.ctx.ModifyPodDisruptionBudget(p.T(), "test-pdb", func(pdb *v1.PodDisruptionBudget) {
pdb.Namespace = "test"
pdb.Spec.Selector = &metaV1.LabelSelector{}
pdb.Spec.Selector.MatchLabels = map[string]string{"foo": "bar"}
pdb.Spec.MinAvailable = &intstr.IntOrString{StrVal: "50%", Type: intstr.String}
})
p.Validate(p.ctx, []templates.TestCase{
{
Param: params.Params{},
Diagnostics: map[string][]diagnostic.Diagnostic{
"test-pdb": {},
},
ExpectInstantiationError: false,
},
})
}
44 changes: 43 additions & 1 deletion tests/checks/pdb-min-available.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,46 @@ spec:
template:
metadata:
labels:
name: cloud-ingress-operator2
name: cloud-ingress-operator2
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: baz3
namespace: bar3
spec:
minAvailable: 1
selector:
matchLabels:
name: cloud-ingress-operator3
---
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: app3
namespace: bar3
spec:
maxReplicaCount: 2
minReplicaCount: 1
scaleTargetRef:
name: foo3
triggers:
- type: cpu
metricType: Utilization
metadata:
value: "50"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: foo3
namespace: bar3
spec:
replicas: 1
selector:
matchLabels:
name: cloud-ingress-operator3
template:
metadata:
labels:
name: cloud-ingress-operator3

0 comments on commit 39d132c

Please sign in to comment.