Skip to content

Commit

Permalink
Generate HPA pointing to ingress segments. (#588)
Browse files Browse the repository at this point in the history
* Generate HPA to segment when segments enabled.

Signed-off-by: Rodrigo Reis <[email protected]>

* Remove non relevant tests.

Signed-off-by: Rodrigo Reis <[email protected]>

* Add ingress metrics to e2e.

Signed-off-by: Rodrigo Reis <[email protected]>

* Cover cases for HPA generation with segments.

Signed-off-by: Rodrigo Reis <[email protected]>

* Simplify tests.

Signed-off-by: Rodrigo Reis <[email protected]>

---------

Signed-off-by: Rodrigo Reis <[email protected]>
  • Loading branch information
gargravarr authored Feb 16, 2024
1 parent c898a7d commit 4200e91
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 133 deletions.
6 changes: 5 additions & 1 deletion controller/stackset.go
Original file line number Diff line number Diff line change
Expand Up @@ -1339,7 +1339,11 @@ func (c *StackSetController) ReconcileStackResources(ctx context.Context, ssc *c
return c.errorEventf(sc.Stack, "FailedManageDeployment", err)
}

err = c.ReconcileStackHPA(ctx, sc.Stack, sc.Resources.HPA, sc.GenerateHPA)
hpaGenerator := sc.GenerateHPA
if ssc.SupportsSegmentTraffic() {
hpaGenerator = sc.GenerateHPAToSegment
}
err = c.ReconcileStackHPA(ctx, sc.Stack, sc.Resources.HPA, hpaGenerator)
if err != nil {
return c.errorEventf(sc.Stack, "FailedManageHPA", err)
}
Expand Down
16 changes: 8 additions & 8 deletions docs/stackset_crd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7986,6 +7986,14 @@ spec:
format: int32
type: integer
path:
description: path is the
relative path of the file
to map the key to. May
not be an absolute path.
May not contain the path
element '..'. May not
start with the string
'..'.
type: string
required:
- key
Expand Down Expand Up @@ -8099,14 +8107,6 @@ spec:
format: int32
type: integer
path:
description: path is the
relative path of the file
to map the key to. May
not be an absolute path.
May not contain the path
element '..'. May not
start with the string
'..'.
type: string
required:
- key
Expand Down
2 changes: 2 additions & 0 deletions e2e/apply/sample-segment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ spec:
metrics:
- type: CPU
averageUtilization: 50
- type: Ingress
average: 20000m
podTemplate:
metadata:
labels:
Expand Down
2 changes: 2 additions & 0 deletions e2e/apply/sample.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ spec:
metrics:
- type: CPU
averageUtilization: 50
- type: Ingress
average: 20000m
podTemplate:
metadata:
labels:
Expand Down
27 changes: 21 additions & 6 deletions pkg/core/stack_resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,25 @@ func (sc *StackContainer) GenerateDeployment() *appsv1.Deployment {
return deployment
}

func (sc *StackContainer) GenerateHPA() (*autoscaling.HorizontalPodAutoscaler, error) {
func (sc *StackContainer) GenerateHPAToSegment() (
*autoscaling.HorizontalPodAutoscaler,
error,
) {
return sc.generateHPA(true)
}

func (sc *StackContainer) GenerateHPA() (
*autoscaling.HorizontalPodAutoscaler,
error,
) {
return sc.generateHPA(false)

}

func (sc *StackContainer) generateHPA(toSegment bool) (
*autoscaling.HorizontalPodAutoscaler,
error,
) {
autoscalerSpec := sc.Stack.Spec.StackSpec.Autoscaler
trafficWeight := sc.actualTrafficWeight

Expand All @@ -254,11 +272,8 @@ func (sc *StackContainer) GenerateHPA() (*autoscaling.HorizontalPodAutoscaler, e
result.Spec.MaxReplicas = autoscalerSpec.MaxReplicas

ingressResourceName := sc.stacksetName
if sc.Resources.IngressSegment != nil {
ingressResourceName = sc.Resources.IngressSegment.Name
}
if sc.Resources.RouteGroupSegment != nil {
ingressResourceName = sc.Resources.RouteGroupSegment.Name
if toSegment {
ingressResourceName = sc.Name() + SegmentSuffix
}

metrics, annotations, err := convertCustomMetrics(
Expand Down
187 changes: 69 additions & 118 deletions pkg/core/stack_resources_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
autoscaling "k8s.io/api/autoscaling/v2"
v1 "k8s.io/api/core/v1"
networking "k8s.io/api/networking/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
)
Expand Down Expand Up @@ -1330,7 +1331,6 @@ func TestGenerateHPA(t *testing.T) {
for _, tc := range []struct {
name string
autoscaler *zv1.Autoscaler
resources StackResources
expectedMinReplicas *int32
expectedMaxReplicas int32
expectedMetrics []autoscaling.MetricSpec
Expand Down Expand Up @@ -1366,122 +1366,6 @@ func TestGenerateHPA(t *testing.T) {
},
expectedBehavior: exampleBehavior,
},
{
name: "HPA in a Stack with ingress segment",
autoscaler: &zv1.Autoscaler{
MinReplicas: &min,
MaxReplicas: max,

Metrics: []zv1.AutoscalerMetrics{
{
Type: zv1.CPUAutoscalerMetric,
AverageUtilization: &utilization,
},
},
Behavior: exampleBehavior,
},
resources: StackResources{
IngressSegment: &networking.Ingress{
ObjectMeta: metav1.ObjectMeta{
Name: "stack-traffic-segment",
},
},
},
expectedMinReplicas: &min,
expectedMaxReplicas: max,
expectedMetrics: []autoscaling.MetricSpec{
{
Type: autoscaling.ResourceMetricSourceType,
Resource: &autoscaling.ResourceMetricSource{
Name: v1.ResourceCPU,
Target: autoscaling.MetricTarget{
Type: autoscaling.UtilizationMetricType,
AverageUtilization: &utilization,
},
},
},
},
expectedBehavior: exampleBehavior,
},
{
name: "HPA in a Stack with routegroup segment",
autoscaler: &zv1.Autoscaler{
MinReplicas: &min,
MaxReplicas: max,

Metrics: []zv1.AutoscalerMetrics{
{
Type: zv1.CPUAutoscalerMetric,
AverageUtilization: &utilization,
},
},
Behavior: exampleBehavior,
},
resources: StackResources{
RouteGroupSegment: &rgv1.RouteGroup{
ObjectMeta: metav1.ObjectMeta{
Name: "stack-traffic-segment",
},
},
},
expectedMinReplicas: &min,
expectedMaxReplicas: max,
expectedMetrics: []autoscaling.MetricSpec{
{
Type: autoscaling.ResourceMetricSourceType,
Resource: &autoscaling.ResourceMetricSource{
Name: v1.ResourceCPU,
Target: autoscaling.MetricTarget{
Type: autoscaling.UtilizationMetricType,
AverageUtilization: &utilization,
},
},
},
},
expectedBehavior: exampleBehavior,
},
{
name: "HPA in a Stack with both routegroup and ingress segment",
autoscaler: &zv1.Autoscaler{
MinReplicas: &min,
MaxReplicas: max,

Metrics: []zv1.AutoscalerMetrics{
{
Type: zv1.CPUAutoscalerMetric,
AverageUtilization: &utilization,
},
},
Behavior: exampleBehavior,
},
resources: StackResources{
IngressSegment: &networking.Ingress{
ObjectMeta: metav1.ObjectMeta{
Name: "stack-traffic-segment",
},
},
RouteGroupSegment: &rgv1.RouteGroup{
ObjectMeta: metav1.ObjectMeta{
Name: "stack-traffic-segment",
},
},
},
expectedMinReplicas: &min,
expectedMaxReplicas: max,
expectedMetrics: []autoscaling.MetricSpec{
{
Type: autoscaling.ResourceMetricSourceType,
Resource: &autoscaling.ResourceMetricSource{
Name: v1.ResourceCPU,
Target: autoscaling.MetricTarget{
Type: autoscaling.UtilizationMetricType,
AverageUtilization: &utilization,
},
},
},
},
expectedBehavior: exampleBehavior,
},
} {
t.Run(tc.name, func(t *testing.T) {
podTemplate := zv1.PodTemplateSpec{
Expand Down Expand Up @@ -1509,7 +1393,6 @@ func TestGenerateHPA(t *testing.T) {
},
},
},
Resources: tc.resources,
}

hpa, err := autoscalerContainer.GenerateHPA()
Expand All @@ -1522,6 +1405,74 @@ func TestGenerateHPA(t *testing.T) {
}
}

func TestGenerateHPAToSegment(t *testing.T) {
for _, tc := range []struct {
name string
metricType zv1.AutoscalerMetricType
expectedRef string
}{
{
name: "HPA metric points to ingress segment",
metricType: zv1.IngressAutoscalerMetric,
expectedRef: "foo-v1-traffic-segment",
},
{
name: "HPA metric points to routeGroup segment",
metricType: zv1.RouteGroupAutoscalerMetric,
expectedRef: "foo-v1-traffic-segment",
},
} {
t.Run(tc.name, func(t *testing.T) {
metricValue := resource.NewQuantity(20, resource.DecimalSI)
var minReplicas int32 = 1

autoScalerContainer := &StackContainer{
Stack: &zv1.Stack{
ObjectMeta: testStackMeta,
Spec: zv1.StackSpecInternal{
StackSpec: zv1.StackSpec{
PodTemplate: zv1.PodTemplateSpec{
EmbeddedObjectMeta: zv1.EmbeddedObjectMeta{
Labels: map[string]string{
"pod-label": "pod-foo",
},
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "foo",
Image: "ghcr.io/zalando/skipper:latest",
},
},
},
},
Autoscaler: &zv1.Autoscaler{
MinReplicas: &minReplicas,
MaxReplicas: 2,
Metrics: []zv1.AutoscalerMetrics{
{
Type: tc.metricType,
Average: metricValue,
},
},
},
},
},
},
}

hpa, err := autoScalerContainer.GenerateHPAToSegment()
require.NoError(t, err)
require.NotEmpty(t, hpa.Spec.Metrics)
require.Equal(
t,
tc.expectedRef,
hpa.Spec.Metrics[0].Object.DescribedObject.Name,
)
})
}
}

func TestGenerateStackStatus(t *testing.T) {
hourAgo := time.Now().Add(-time.Hour)

Expand Down

0 comments on commit 4200e91

Please sign in to comment.