Skip to content

Commit

Permalink
controllers/hpa: adds supports for v2/autoscaling API
Browse files Browse the repository at this point in the history
operator will switch for it at kubernetes version >= 1.26
#583
  • Loading branch information
f41gh7 committed Jan 16, 2023
1 parent 1aeaca3 commit 772151e
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 27 deletions.
44 changes: 31 additions & 13 deletions controllers/factory/builders.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"fmt"
"github.com/VictoriaMetrics/operator/controllers/factory/k8stools"
v2 "k8s.io/api/autoscaling/v2"
"k8s.io/api/autoscaling/v2beta2"
policyv1 "k8s.io/api/policy/v1"
policyv1beta1 "k8s.io/api/policy/v1beta1"
Expand Down Expand Up @@ -402,36 +403,53 @@ func buildProbe(container v1.Container, cr probeCRD) v1.Container {
return container
}

func buildHPASpec(targetRef v2beta2.CrossVersionObjectReference, spec *victoriametricsv1beta1.EmbeddedHPA, or []metav1.OwnerReference, lbls map[string]string, namespace string) *v2beta2.HorizontalPodAutoscaler {
return &v2beta2.HorizontalPodAutoscaler{
func buildHPASpec(targetRef v2beta2.CrossVersionObjectReference, spec *victoriametricsv1beta1.EmbeddedHPA, or []metav1.OwnerReference, lbls map[string]string, namespace string) client.Object {
if k8stools.IsHPAV2BetaSupported() {
return &v2beta2.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{
Name: targetRef.Name,
Namespace: namespace,
Labels: lbls,
OwnerReferences: or,
},
Spec: v2beta2.HorizontalPodAutoscalerSpec{
MaxReplicas: spec.MaxReplicas,
MinReplicas: spec.MinReplicas,
ScaleTargetRef: targetRef,
Metrics: spec.Metrics,
Behavior: spec.Behaviour,
},
}
}

return &v2.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{
Name: targetRef.Name,
Namespace: namespace,
Labels: lbls,
OwnerReferences: or,
},
Spec: v2beta2.HorizontalPodAutoscalerSpec{
Spec: v2.HorizontalPodAutoscalerSpec{
MaxReplicas: spec.MaxReplicas,
MinReplicas: spec.MinReplicas,
ScaleTargetRef: targetRef,
Metrics: spec.Metrics,
Behavior: spec.Behaviour,
ScaleTargetRef: v2.CrossVersionObjectReference(targetRef),
Metrics: *k8stools.MustConvertObjectVersionsJSON[[]v2beta2.MetricSpec, []v2.MetricSpec](&spec.Metrics, "v2beta/autoscaling/metricsSpec"),
Behavior: k8stools.MustConvertObjectVersionsJSON[v2beta2.HorizontalPodAutoscalerBehavior, v2.HorizontalPodAutoscalerBehavior](spec.Behaviour, "v2beta/autoscaling/behaviorSpec"),
},
}

}

func reconcileHPA(ctx context.Context, rclient client.Client, targetHPA *v2beta2.HorizontalPodAutoscaler) error {
var existHPA v2beta2.HorizontalPodAutoscaler
if err := rclient.Get(ctx, types.NamespacedName{Name: targetHPA.Name, Namespace: targetHPA.Namespace}, &existHPA); err != nil {
func reconcileHPA(ctx context.Context, rclient client.Client, targetHPA client.Object) error {
existHPA := k8stools.NewHPAEmptyObject()
if err := rclient.Get(ctx, types.NamespacedName{Name: targetHPA.GetName(), Namespace: targetHPA.GetNamespace()}, existHPA); err != nil {
if errors.IsNotFound(err) {
log.Info("creating new HPA for vminsert")
return rclient.Create(ctx, targetHPA)
}
}

targetHPA.ResourceVersion = existHPA.ResourceVersion
targetHPA.SetResourceVersion(existHPA.GetResourceVersion())
victoriametricsv1beta1.MergeFinalizers(targetHPA, victoriametricsv1beta1.FinalizerName)
targetHPA.Annotations = labels.Merge(existHPA.Annotations, targetHPA.Annotations)
targetHPA.SetAnnotations(labels.Merge(existHPA.GetAnnotations(), targetHPA.GetAnnotations()))

return rclient.Update(ctx, targetHPA)
}
Expand Down
23 changes: 11 additions & 12 deletions controllers/factory/finalize/vmcluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,20 @@ package finalize

import (
"context"

victoriametricsv1beta1 "github.com/VictoriaMetrics/operator/api/v1beta1"
"github.com/VictoriaMetrics/operator/controllers/factory/k8stools"
appsv1 "k8s.io/api/apps/v1"
"k8s.io/api/autoscaling/v2beta2"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"sigs.k8s.io/controller-runtime/pkg/client"
)

// HPADelete handles case, when user wants to remove HPA configuration from cluster config.
func HPADelete(ctx context.Context, rclient client.Client, objectName, objectNamespace string) error {
hpa := &v2beta2.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{
Name: objectName,
Namespace: objectNamespace,
},
}
hpa := k8stools.NewHPAEmptyObject(func(obj client.Object) {
obj.SetName(objectName)
obj.SetNamespace(objectNamespace)
})

if err := removeFinalizeObjByName(ctx, rclient, hpa, objectName, objectNamespace); err != nil {
return err
}
Expand All @@ -31,6 +27,7 @@ func HPADelete(ctx context.Context, rclient client.Client, objectName, objectNam

func OnVMClusterDelete(ctx context.Context, rclient client.Client, crd *victoriametricsv1beta1.VMCluster) error {
// check deployment

if crd.Spec.VMInsert != nil {
obj := crd.Spec.VMInsert
if err := removeFinalizeObjByName(ctx, rclient, &appsv1.Deployment{}, obj.GetNameWithPrefix(crd.Name), crd.Namespace); err != nil {
Expand All @@ -45,7 +42,7 @@ func OnVMClusterDelete(ctx context.Context, rclient client.Client, crd *victoria
return err
}
}
if err := removeFinalizeObjByName(ctx, rclient, &v2beta2.HorizontalPodAutoscaler{}, obj.GetNameWithPrefix(crd.Name), crd.Namespace); err != nil {
if err := removeFinalizeObjByName(ctx, rclient, k8stools.NewHPAEmptyObject(), obj.GetNameWithPrefix(crd.Name), crd.Namespace); err != nil {
return err
}

Expand All @@ -54,6 +51,7 @@ func OnVMClusterDelete(ctx context.Context, rclient client.Client, crd *victoria
return err
}
}

if crd.Spec.VMSelect != nil {
obj := crd.Spec.VMSelect
if err := removeFinalizeObjByName(ctx, rclient, &appsv1.StatefulSet{}, obj.GetNameWithPrefix(crd.Name), crd.Namespace); err != nil {
Expand All @@ -70,7 +68,8 @@ func OnVMClusterDelete(ctx context.Context, rclient client.Client, crd *victoria
return err
}

if err := removeFinalizeObjByName(ctx, rclient, &v2beta2.HorizontalPodAutoscaler{}, obj.GetNameWithPrefix(crd.Name), crd.Namespace); err != nil {
// remove hpa
if err := removeFinalizeObjByName(ctx, rclient, k8stools.NewHPAEmptyObject(), obj.GetNameWithPrefix(crd.Name), crd.Namespace); err != nil {
return err
}

Expand Down
44 changes: 44 additions & 0 deletions controllers/factory/k8stools/version.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package k8stools

import (
"encoding/json"
"fmt"
"k8s.io/api/autoscaling/v2beta2"
"k8s.io/apimachinery/pkg/version"
"sigs.k8s.io/controller-runtime/pkg/client"
"strconv"
"strings"
)
Expand Down Expand Up @@ -54,3 +57,44 @@ func IsPDBV1APISupported() bool {
}
return false
}

// IsHPAV2BetaSupported checks if new
// Beta deprecated since 1.26
// https://kubernetes.io/blog/2021/12/07/kubernetes-1-23-release-announcement/#horizontalpodautoscaler-v2-graduates-to-ga
func IsHPAV2BetaSupported() bool {
if ServerMajorVersion == 1 && ServerMinorVersion < 26 {
return true
}
return false
}

// NewHPAEmptyObject returns HorizontalPodAutoscaler object for given kubernetes version
func NewHPAEmptyObject(opts ...func(obj client.Object)) client.Object {
var hpa client.Object = &v2beta2.HorizontalPodAutoscaler{}
if !IsHPAV2BetaSupported() {
hpa = &v2beta2.HorizontalPodAutoscaler{}
}
for _, opt := range opts {
opt(hpa)
}
return hpa
}

// MustConvertObjectVersionsJSON objects with json serialize and deserialize
// it could be used only for converting BETA apis to Stable version
func MustConvertObjectVersionsJSON[A, B any](src *A, objectName string) *B {
var dst B
if src == nil {
return nil
}
srcB, err := json.Marshal(src)
if err != nil {
log.Error(err, "BUG, cannot serialize object for API", "APIObject", objectName, "object", src)
return nil
}
if err := json.Unmarshal(srcB, &dst); err != nil {
log.Error(err, "BUG, cannot parse object for API", "APIObject", objectName, "object", string(srcB))
return nil
}
return &dst
}
3 changes: 1 addition & 2 deletions internal/manager/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import (
"github.com/prometheus/client_golang/prometheus"
"github.com/spf13/pflag"
v12 "k8s.io/api/apps/v1"
"k8s.io/api/autoscaling/v2beta2"
v1 "k8s.io/api/core/v1"
"k8s.io/api/policy/v1beta1"
metav1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
Expand Down Expand Up @@ -116,7 +115,7 @@ func RunManager(ctx context.Context) error {
LeaderElection: *enableLeaderElection,
LeaderElectionID: "57410f0d.victoriametrics.com",
ClientDisableCacheFor: []client.Object{&v1.Secret{}, &v1.ConfigMap{}, &v1.Pod{}, &v12.Deployment{},
&v12.StatefulSet{}, &v2beta2.HorizontalPodAutoscaler{},
&v12.StatefulSet{},
&v1beta1.PodSecurityPolicy{}, &v1beta1.PodDisruptionBudget{}},
Namespace: config.MustGetWatchNamespace(),
})
Expand Down

0 comments on commit 772151e

Please sign in to comment.