Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion docs/gitbook/usage/how-it-works.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ in the primary autoscaler when a rollout for the deployment starts and completes
Optionally, you can create two HPAs, one for canary and one for the primary to update the HPA without
doing a new rollout. As the canary deployment will be scaled to 0, the HPA on the canary will be inactive.

**Note** Flagger requires `autoscaling/v2` or `autoscaling/v2beta2` API version for HPAs.
**Note** Flagger requires `autoscaling/v2` API version for HPAs.

The progress deadline represents the maximum time in seconds for the canary deployment to
make progress before it is rolled back, defaults to ten minutes.
Expand Down
2 changes: 0 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ require (
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.21.0 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/google/gnostic-models v0.7.0 // indirect
github.com/google/go-containerregistry v0.20.3 // indirect
github.com/google/s2a-go v0.1.9 // indirect
Expand All @@ -70,7 +69,6 @@ require (
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/oapi-codegen/runtime v1.0.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.66.1 // indirect
Expand Down
71 changes: 5 additions & 66 deletions go.sum

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions pkg/apis/keda/v1alpha1/scaledobject.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ limitations under the License.
package v1alpha1

import (
autoscalingv2beta2 "k8s.io/api/autoscaling/v2beta2"
autoscalingv2 "k8s.io/api/autoscaling/v2"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

Expand Down Expand Up @@ -106,7 +106,7 @@ type HorizontalPodAutoscalerConfig struct {
// +optional
Name string `json:"name,omitempty"`
// +optional
Behavior *autoscalingv2beta2.HorizontalPodAutoscalerBehavior `json:"behavior,omitempty"`
Behavior *autoscalingv2.HorizontalPodAutoscalerBehavior `json:"behavior,omitempty"`
}

// ScaleTarget holds the a reference to the scale target Object
Expand All @@ -129,7 +129,7 @@ type ScaleTriggers struct {
// +optional
AuthenticationRef *ScaledObjectAuthRef `json:"authenticationRef,omitempty"`
// +optional
MetricType autoscalingv2beta2.MetricTargetType `json:"metricType,omitempty"`
MetricType autoscalingv2.MetricTargetType `json:"metricType,omitempty"`
}

// +k8s:openapi-gen=true
Expand Down
4 changes: 2 additions & 2 deletions pkg/apis/keda/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 0 additions & 8 deletions pkg/canary/deployment_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,14 +191,6 @@ func (c *DeploymentController) ScaleFromZero(cd *flaggerv1.Canary) error {
if hpa.Spec.MinReplicas != nil && *hpa.Spec.MinReplicas > 1 {
replicas = hpa.Spec.MinReplicas
}
} else {
// fallback to v2beta2
hpa, err := c.kubeClient.AutoscalingV2beta2().HorizontalPodAutoscalers(cd.Namespace).Get(context.TODO(), cd.Spec.AutoscalerRef.Name, metav1.GetOptions{})
if err == nil {
if hpa.Spec.MinReplicas != nil && *hpa.Spec.MinReplicas > 1 {
replicas = hpa.Spec.MinReplicas
}
}
}
} else if cd.Spec.AutoscalerRef.Kind == "ScaledObject" {
so, err := c.flaggerClient.KedaV1alpha1().ScaledObjects(cd.Namespace).Get(context.TODO(), cd.Spec.AutoscalerRef.Name, metav1.GetOptions{})
Expand Down
2 changes: 1 addition & 1 deletion pkg/canary/deployment_fixture_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,7 @@ func newDeploymentControllerTestCanary(cc canaryConfigs) *flaggerv1.Canary {
},
AutoscalerRef: &flaggerv1.AutoscalerReference{
Name: "podinfo",
APIVersion: "autoscaling/v2beta2",
APIVersion: "autoscaling/v2",
Kind: "HorizontalPodAutoscaler",
}, Service: flaggerv1.CanaryService{
Port: 9898,
Expand Down
109 changes: 3 additions & 106 deletions pkg/canary/hpa_reconciler.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"github.com/google/go-cmp/cmp"
"go.uber.org/zap"
hpav2 "k8s.io/api/autoscaling/v2"
hpav2beta2 "k8s.io/api/autoscaling/v2beta2"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
Expand All @@ -35,28 +34,19 @@ func (hr *HPAReconciler) ReconcilePrimaryScaler(cd *flaggerv1.Canary, init bool)
}

func (hr *HPAReconciler) reconcilePrimaryHpa(cd *flaggerv1.Canary, init bool) error {
var betaHpa *hpav2beta2.HorizontalPodAutoscaler
hpa, err := hr.kubeClient.AutoscalingV2().HorizontalPodAutoscalers(cd.Namespace).Get(context.TODO(), cd.Spec.AutoscalerRef.Name, metav1.GetOptions{})
if err != nil {
hpa = nil
hr.logger.Debugf("v2 HorizontalPodAutoscaler %s.%s get query error: %s; falling back to v2beta2",
hr.logger.Debugf("v2 HorizontalPodAutoscaler %s.%s get query error: %s;",
cd.Namespace, cd.Spec.AutoscalerRef.Name, err)
var betaErr error
betaHpa, betaErr = hr.kubeClient.AutoscalingV2beta2().HorizontalPodAutoscalers(cd.Namespace).Get(context.TODO(), cd.Spec.AutoscalerRef.Name, metav1.GetOptions{})
if betaErr != nil {
return fmt.Errorf("HorizontalPodAutoscaler %s.%s get query error for both v2beta2: %s and v2: %s",
cd.Spec.AutoscalerRef.Name, cd.Namespace, betaErr, err)
}
return fmt.Errorf("HorizontalPodAutoscaler %s.%s get query error: %s",
cd.Spec.AutoscalerRef.Name, cd.Namespace, err)
}

if hpa != nil {
if err = hr.reconcilePrimaryHpaV2(cd, hpa, init); err != nil {
return err
}
} else if betaHpa != nil {
if err = hr.reconcilePrimaryHpaV2Beta2(cd, betaHpa, init); err != nil {
return err
}
}

return nil
Expand Down Expand Up @@ -155,99 +145,6 @@ func (hr *HPAReconciler) reconcilePrimaryHpaV2(cd *flaggerv1.Canary, hpa *hpav2.
return nil
}

func (hr *HPAReconciler) reconcilePrimaryHpaV2Beta2(cd *flaggerv1.Canary, hpa *hpav2beta2.HorizontalPodAutoscaler, init bool) error {
primaryName := fmt.Sprintf("%s-primary", cd.Spec.TargetRef.Name)

hpaSpec := hpav2beta2.HorizontalPodAutoscalerSpec{
ScaleTargetRef: hpav2beta2.CrossVersionObjectReference{
Name: primaryName,
Kind: hpa.Spec.ScaleTargetRef.Kind,
APIVersion: hpa.Spec.ScaleTargetRef.APIVersion,
},
MinReplicas: hpa.Spec.MinReplicas,
MaxReplicas: hpa.Spec.MaxReplicas,
Metrics: hpa.Spec.Metrics,
Behavior: hpa.Spec.Behavior,
}

if replicas := cd.Spec.AutoscalerRef.PrimaryScalerReplicas; replicas != nil {
if minReplicas := replicas.MinReplicas; minReplicas != nil {
hpaSpec.MinReplicas = minReplicas
}
if maxReplicas := replicas.MaxReplicas; maxReplicas != nil {
hpaSpec.MaxReplicas = *maxReplicas
}
}

primaryHpaName := fmt.Sprintf("%s-primary", cd.Spec.AutoscalerRef.Name)
primaryHpa, err := hr.kubeClient.AutoscalingV2beta2().HorizontalPodAutoscalers(cd.Namespace).Get(context.TODO(), primaryHpaName, metav1.GetOptions{})

// create HPA
if errors.IsNotFound(err) {
primaryHpa = &hpav2beta2.HorizontalPodAutoscaler{
ObjectMeta: makeObjectMeta(primaryHpaName, hpa.Labels, cd),
Spec: hpaSpec,
}

_, err = hr.kubeClient.AutoscalingV2beta2().HorizontalPodAutoscalers(cd.Namespace).Create(context.TODO(), primaryHpa, metav1.CreateOptions{})
if err != nil {
return fmt.Errorf("creating HorizontalPodAutoscaler v2beta2 %s.%s failed: %w",
primaryHpa.Name, primaryHpa.Namespace, err)
}
hr.logger.With("canary", fmt.Sprintf("%s.%s", cd.Name, cd.Namespace)).Infof(
"HorizontalPodAutoscaler v2beta2 %s.%s created", primaryHpa.GetName(), cd.Namespace)
return nil
} else if err != nil {
return fmt.Errorf("HorizontalPodAutoscaler v2beta2 %s.%s get query failed: %w",
primaryHpa.Name, primaryHpa.Namespace, err)
}

// update HPA
if !init && primaryHpa != nil {
targetFields := hpaFields{
metrics: hpaSpec.Metrics,
behavior: hpaSpec.Behavior,
annotations: hpa.Annotations,
labels: hpa.Labels,
min: hpaSpec.MinReplicas,
max: hpaSpec.MaxReplicas,
}
primaryFields := hpaFields{
metrics: primaryHpa.Spec.Metrics,
behavior: primaryHpa.Spec.Behavior,
annotations: primaryHpa.Annotations,
labels: primaryHpa.Labels,
min: primaryHpa.Spec.MinReplicas,
max: primaryHpa.Spec.MaxReplicas,
}
if hasHPAChanged(targetFields, primaryFields) {
err = retry.RetryOnConflict(retry.DefaultRetry, func() error {
primaryHpa, err := hr.kubeClient.AutoscalingV2beta2().HorizontalPodAutoscalers(cd.Namespace).Get(context.TODO(), primaryHpaName, metav1.GetOptions{})
if err != nil {
return err
}
primaryHpaClone := primaryHpa.DeepCopy()
primaryHpaClone.Spec.MaxReplicas = hpaSpec.MaxReplicas
primaryHpaClone.Spec.MinReplicas = hpaSpec.MinReplicas
primaryHpaClone.Spec.Metrics = hpaSpec.Metrics
primaryHpaClone.Spec.Behavior = hpaSpec.Behavior

hr.updateObjectMeta(primaryHpaClone.ObjectMeta, hpa.ObjectMeta)

_, err = hr.kubeClient.AutoscalingV2beta2().HorizontalPodAutoscalers(cd.Namespace).Update(context.TODO(), primaryHpaClone, metav1.UpdateOptions{})
return err
})
if err != nil {
return fmt.Errorf("updating HorizontalPodAutoscaler v2beta2 %s.%s failed: %w",
primaryHpa.Name, primaryHpa.Namespace, err)
}
hr.logger.With("canary", fmt.Sprintf("%s.%s", cd.Name, cd.Namespace)).
Infof("HorizontalPodAutoscaler v2beta2 %s.%s updated", primaryHpa.GetName(), cd.Namespace)
}
}
return nil
}

func (hr *HPAReconciler) PauseTargetScaler(cd *flaggerv1.Canary) error {
return nil
}
Expand Down
59 changes: 1 addition & 58 deletions pkg/canary/hpa_reconciler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,36 +8,24 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
hpav2 "k8s.io/api/autoscaling/v2"
hpav2beta2 "k8s.io/api/autoscaling/v2beta2"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func Test_reconcilePrimaryHpa(t *testing.T) {
mocks := newScalerReconcilerFixture(scalerConfig{
targetName: "podinfo",
scaler: "HorizontalPodAutoscaler",
// avoid creating a v2 HPA.
excludeObjs: []string{"HPAV2"},
})
hpaReconciler := mocks.scalerReconciler.(*HPAReconciler)

err := hpaReconciler.reconcilePrimaryHpa(mocks.canary, true)
require.NoError(t, err)

// assert that we fallback to v2beta2, when HPAv2 fails.
_, err = mocks.kubeClient.AutoscalingV2().HorizontalPodAutoscalers("default").Get(context.TODO(), "podinfo-primary", metav1.GetOptions{})
assert.True(t, errors.IsNotFound(err))

hpa, err := mocks.kubeClient.AutoscalingV2beta2().HorizontalPodAutoscalers("default").Get(context.TODO(), "podinfo-primary", metav1.GetOptions{})
require.NoError(t, err)
require.NotNil(t, hpa)

mocks = newScalerReconcilerFixture(scalerConfig{
targetName: "podinfo",
scaler: "HorizontalPodAutoscaler",
// avoid creating _any_ HPAs.
excludeObjs: []string{"HPAV2", "HPAV2Beta2"},
excludeObjs: []string{"HPAV2"},
})
hpaReconciler = mocks.scalerReconciler.(*HPAReconciler)
// assert that we return an error if no HPAs are found.
Expand Down Expand Up @@ -88,48 +76,3 @@ func Test_reconcilePrimaryHpaV2(t *testing.T) {
assert.Equal(t, primaryHPA.Spec.MinReplicas, mocks.canary.Spec.AutoscalerRef.PrimaryScalerReplicas.MinReplicas)
assert.Equal(t, primaryHPA.Spec.MaxReplicas, *mocks.canary.Spec.AutoscalerRef.PrimaryScalerReplicas.MaxReplicas)
}

func Test_reconcilePrimaryHpaV2Beta2(t *testing.T) {
mocks := newScalerReconcilerFixture(scalerConfig{
targetName: "podinfo",
scaler: "HorizontalPodAutoscaler",
})

hpaReconciler := mocks.scalerReconciler.(*HPAReconciler)

hpa, err := mocks.kubeClient.AutoscalingV2beta2().HorizontalPodAutoscalers("default").Get(context.TODO(), "podinfo", metav1.GetOptions{})
require.NoError(t, err)

err = hpaReconciler.reconcilePrimaryHpaV2Beta2(mocks.canary, hpa, true)
require.NoError(t, err)

primaryHPA, err := mocks.kubeClient.AutoscalingV2beta2().HorizontalPodAutoscalers("default").Get(context.TODO(), "podinfo-primary", metav1.GetOptions{})
require.NoError(t, err)
assert.Equal(t, int(*primaryHPA.Spec.Metrics[0].Resource.Target.AverageUtilization), 99)

hpa.Spec.Metrics[0].Resource.Target = hpav2beta2.MetricTarget{AverageUtilization: int32p(50)}
hpa.Spec.MaxReplicas = 10
_, err = mocks.kubeClient.AutoscalingV2beta2().HorizontalPodAutoscalers("default").Update(context.TODO(), hpa, metav1.UpdateOptions{})
require.NoError(t, err)

err = hpaReconciler.reconcilePrimaryHpaV2Beta2(mocks.canary, hpa, false)
require.NoError(t, err)

primaryHPA, err = mocks.kubeClient.AutoscalingV2beta2().HorizontalPodAutoscalers("default").Get(context.TODO(), "podinfo-primary", metav1.GetOptions{})
require.NoError(t, err)
assert.Equal(t, int(*primaryHPA.Spec.Metrics[0].Resource.Target.AverageUtilization), 50)
assert.Equal(t, int(primaryHPA.Spec.MaxReplicas), 10)

// Test reconcile with PrimaryHorizontalPodAutoscalerOverride
mocks.canary.Spec.AutoscalerRef.PrimaryScalerReplicas = &flaggerv1.ScalerReplicas{
MinReplicas: int32p(2),
MaxReplicas: int32p(15),
}
err = hpaReconciler.reconcilePrimaryHpaV2Beta2(mocks.canary, hpa, false)
require.NoError(t, err)

primaryHPA, err = mocks.kubeClient.AutoscalingV2beta2().HorizontalPodAutoscalers("default").Get(context.TODO(), "podinfo-primary", metav1.GetOptions{})
require.NoError(t, err)
assert.Equal(t, primaryHPA.Spec.MinReplicas, mocks.canary.Spec.AutoscalerRef.PrimaryScalerReplicas.MinReplicas)
assert.Equal(t, primaryHPA.Spec.MaxReplicas, *mocks.canary.Spec.AutoscalerRef.PrimaryScalerReplicas.MaxReplicas)
}
39 changes: 0 additions & 39 deletions pkg/canary/scaler_reconciler_fixture_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package canary
import (
"go.uber.org/zap"
hpav2 "k8s.io/api/autoscaling/v2"
hpav2beta2 "k8s.io/api/autoscaling/v2beta2"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/kubernetes"
Expand Down Expand Up @@ -40,7 +39,6 @@ func newScalerReconcilerFixture(cfg scalerConfig) scalerReconcilerFixture {

kubeClient := fake.NewSimpleClientset(
newScalerReconcilerTestHPAV2(),
newScalerReconcilerTestHPAV2Beta2(),
)
for _, obj := range cfg.excludeObjs {
if obj == "HPAV2" {
Expand All @@ -50,13 +48,6 @@ func newScalerReconcilerFixture(cfg scalerConfig) scalerReconcilerFixture {
Resource: "horizontalpodautoscalers",
}, "default", "podinfo")
}
if obj == "HPAV2Beta2" {
kubeClient.Tracker().Delete(schema.GroupVersionResource{
Group: "autoscaling",
Version: "v2beta2",
Resource: "horizontalpodautoscalers",
}, "default", "podinfo")
}
}

logger, _ := logger.NewLogger("debug")
Expand Down Expand Up @@ -88,36 +79,6 @@ func newScalerReconcilerFixture(cfg scalerConfig) scalerReconcilerFixture {
}
}

func newScalerReconcilerTestHPAV2Beta2() *hpav2beta2.HorizontalPodAutoscaler {
h := &hpav2beta2.HorizontalPodAutoscaler{
TypeMeta: metav1.TypeMeta{APIVersion: hpav2beta2.SchemeGroupVersion.String()},
ObjectMeta: metav1.ObjectMeta{
Namespace: "default",
Name: "podinfo",
},
Spec: hpav2beta2.HorizontalPodAutoscalerSpec{
ScaleTargetRef: hpav2beta2.CrossVersionObjectReference{
Name: "podinfo",
APIVersion: "apps/v1",
Kind: "Deployment",
},
Metrics: []hpav2beta2.MetricSpec{
{
Type: "Resource",
Resource: &hpav2beta2.ResourceMetricSource{
Name: "cpu",
Target: hpav2beta2.MetricTarget{
AverageUtilization: int32p(99),
},
},
},
},
},
}

return h
}

func newScalerReconcilerTestHPAV2() *hpav2.HorizontalPodAutoscaler {
h := &hpav2.HorizontalPodAutoscaler{
TypeMeta: metav1.TypeMeta{APIVersion: hpav2.SchemeGroupVersion.String()},
Expand Down
Loading