diff --git a/apis/autoscaling/v1alpha1/horizontalportrait_types.go b/apis/autoscaling/v1alpha1/horizontalportrait_types.go index 2c1d587..09b6401 100644 --- a/apis/autoscaling/v1alpha1/horizontalportrait_types.go +++ b/apis/autoscaling/v1alpha1/horizontalportrait_types.go @@ -20,6 +20,7 @@ import ( k8sautoscalingv2 "k8s.io/api/autoscaling/v2" batchv1 "k8s.io/api/batch/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" ) //+kubebuilder:object:root=true @@ -46,9 +47,16 @@ type HorizontalPortraitSpec struct { // PortraitSpec defines general specs of portrait. type PortraitSpec struct { - // PortraitType is the type of portrait. Different type has different portrait generating logic. + // PortraitType is the type of portrait. + // Different type has different semantics with different portrait generating logic. PortraitType PortraitType `json:"portraitType"` + // ExternalGeneratorName is the identifier of external controller which shall generate this portrait. + // External controllers should use this field to filter portraits to generate. + // Kapacity's built-in portrait generators would ignore portraits with this field set. + // +optional + ExternalGeneratorName string `json:"externalGeneratorName,omitempty"` + // Metrics contains the specifications for which to use to generate the portrait. // +optional Metrics []MetricSpec `json:"metrics,omitempty"` @@ -121,10 +129,10 @@ type PortraitAlgorithm struct { KubeHPA *KubeHPAPortraitAlgorithm `json:"kubeHPA,omitempty"` // Config is the general configuration data for arbitrary algorithm those - // used by external user-defined portraits. - // TODO: consider if we can make it structural + // used by external portrait generators. // +optional - Config map[string]string `json:"config,omitempty"` + // +kubebuilder:pruning:PreserveUnknownFields + Config *runtime.RawExtension `json:"config,omitempty"` } type PortraitAlgorithmType string @@ -137,32 +145,6 @@ const ( KubeHPAPortraitAlgorithmType PortraitAlgorithmType = "KubeHPA" ) -// KubeHPAPortraitAlgorithm defines parameters of KubeHPA algorithm. -type KubeHPAPortraitAlgorithm struct { - // SyncPeriod is the period for syncing the portrait. - // +optional - // +kubebuilder:default="15s" - SyncPeriod metav1.Duration `json:"syncPeriod"` - - // Tolerance is the tolerance for when resource usage suggests upscaling/downscaling. - // Should be a string formatted float64 number. - // +optional - // +kubebuilder:default="0.1" - Tolerance string `json:"tolerance"` - - // CPUInitializationPeriod is the period after pod start when CPU samples might be skipped. - // +optional - // +kubebuilder:default="5m" - CPUInitializationPeriod metav1.Duration `json:"cpuInitializationPeriod"` - - // InitialReadinessDelay is period after pod start during which readiness changes - // are treated as readiness being set for the first time. The only effect of this is that - // HPA will disregard CPU samples from unready pods that had last readiness change during that period. - // +optional - // +kubebuilder:default="30s" - InitialReadinessDelay metav1.Duration `json:"initialReadinessDelay"` -} - // ExternalJobPortraitAlgorithm defines configurations of ExternalJob algorithm. type ExternalJobPortraitAlgorithm struct { // Job is the external job that runs the algorithm. @@ -226,6 +208,32 @@ const ( // ConfigMapPortraitAlgorithmResultSource defines configurations of ConfigMap result source. type ConfigMapPortraitAlgorithmResultSource struct{} +// KubeHPAPortraitAlgorithm defines parameters of KubeHPA algorithm. +type KubeHPAPortraitAlgorithm struct { + // SyncPeriod is the period for syncing the portrait. + // +optional + // +kubebuilder:default="15s" + SyncPeriod metav1.Duration `json:"syncPeriod"` + + // Tolerance is the tolerance for when resource usage suggests upscaling/downscaling. + // Should be a string formatted float64 number. + // +optional + // +kubebuilder:default="0.1" + Tolerance string `json:"tolerance"` + + // CPUInitializationPeriod is the period after pod start when CPU samples might be skipped. + // +optional + // +kubebuilder:default="5m" + CPUInitializationPeriod metav1.Duration `json:"cpuInitializationPeriod"` + + // InitialReadinessDelay is period after pod start during which readiness changes + // are treated as readiness being set for the first time. The only effect of this is that + // HPA will disregard CPU samples from unready pods that had last readiness change during that period. + // +optional + // +kubebuilder:default="30s" + InitialReadinessDelay metav1.Duration `json:"initialReadinessDelay"` +} + // HorizontalPortraitStatus defines the observed state of HorizontalPortrait. type HorizontalPortraitStatus struct { // PortraitData is the data of generated portrait. diff --git a/apis/autoscaling/v1alpha1/zz_generated.deepcopy.go b/apis/autoscaling/v1alpha1/zz_generated.deepcopy.go index 0b5713b..8b77fb1 100644 --- a/apis/autoscaling/v1alpha1/zz_generated.deepcopy.go +++ b/apis/autoscaling/v1alpha1/zz_generated.deepcopy.go @@ -23,7 +23,7 @@ package v1alpha1 import ( "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime" ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. @@ -690,10 +690,8 @@ func (in *PortraitAlgorithm) DeepCopyInto(out *PortraitAlgorithm) { } if in.Config != nil { in, out := &in.Config, &out.Config - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } + *out = new(runtime.RawExtension) + (*in).DeepCopyInto(*out) } } diff --git a/config/crd/bases/autoscaling.kapacitystack.io_horizontalportraits.yaml b/config/crd/bases/autoscaling.kapacitystack.io_horizontalportraits.yaml index 1d05242..82bcfdc 100644 --- a/config/crd/bases/autoscaling.kapacitystack.io_horizontalportraits.yaml +++ b/config/crd/bases/autoscaling.kapacitystack.io_horizontalportraits.yaml @@ -44,12 +44,10 @@ spec: the portrait. properties: config: - additionalProperties: - type: string - description: 'Config is the general configuration data for arbitrary - algorithm those used by external user-defined portraits. TODO: - consider if we can make it structural' + description: Config is the general configuration data for arbitrary + algorithm those used by external portrait generators. type: object + x-kubernetes-preserve-unknown-fields: true externalJob: description: ExternalJob is the configuration for ExternalJob algorithm. @@ -15937,6 +15935,12 @@ spec: required: - type type: object + externalGeneratorName: + description: ExternalGeneratorName is the identifier of external controller + which shall generate this portrait. External controllers should + use this field to filter portraits to generate. Kapacity's built-in + portrait generators would ignore portraits with this field set. + type: string metrics: description: Metrics contains the specifications for which to use to generate the portrait. @@ -16423,7 +16427,7 @@ spec: type: array portraitType: description: PortraitType is the type of portrait. Different type - has different portrait generating logic. + has different semantics with different portrait generating logic. type: string scaleTargetRef: description: ScaleTargetRef points to the target resource to scale. diff --git a/config/crd/bases/autoscaling.kapacitystack.io_intelligenthorizontalpodautoscalers.yaml b/config/crd/bases/autoscaling.kapacitystack.io_intelligenthorizontalpodautoscalers.yaml index 87bd341..9b00d23 100644 --- a/config/crd/bases/autoscaling.kapacitystack.io_intelligenthorizontalpodautoscalers.yaml +++ b/config/crd/bases/autoscaling.kapacitystack.io_intelligenthorizontalpodautoscalers.yaml @@ -301,12 +301,11 @@ spec: to generate the portrait. properties: config: - additionalProperties: - type: string - description: 'Config is the general configuration data - for arbitrary algorithm those used by external user-defined - portraits. TODO: consider if we can make it structural' + description: Config is the general configuration data + for arbitrary algorithm those used by external portrait + generators. type: object + x-kubernetes-preserve-unknown-fields: true externalJob: description: ExternalJob is the configuration for ExternalJob algorithm. @@ -25277,6 +25276,13 @@ spec: required: - type type: object + externalGeneratorName: + description: ExternalGeneratorName is the identifier of + external controller which shall generate this portrait. + External controllers should use this field to filter portraits + to generate. Kapacity's built-in portrait generators would + ignore portraits with this field set. + type: string metrics: description: Metrics contains the specifications for which to use to generate the portrait. @@ -25800,7 +25806,8 @@ spec: type: array portraitType: description: PortraitType is the type of portrait. Different - type has different portrait generating logic. + type has different semantics with different portrait generating + logic. type: string required: - algorithm diff --git a/controllers/autoscaling/horizontalportrait_controller.go b/controllers/autoscaling/horizontalportrait_controller.go index f61c2da..3339b15 100644 --- a/controllers/autoscaling/horizontalportrait_controller.go +++ b/controllers/autoscaling/horizontalportrait_controller.go @@ -151,8 +151,11 @@ func (r *HorizontalPortraitReconciler) Reconcile(ctx context.Context, req ctrl.R portraitGenerator, ok := r.PortraitGenerators[hp.Spec.PortraitType] if !ok { - // this should not happen because we have watch predicates - l.Info("unknown portrait type, ignore", "portraitType", hp.Spec.PortraitType) + err := fmt.Errorf("unknown portrait type %q", hp.Spec.PortraitType) + l.Error(err, "failed to get portrait generator") + r.errorOut(ctx, hp, hpStatusOriginal, autoscalingv1alpha1.PortraitGenerated, metav1.ConditionFalse, + "FailedGetPortraitGenerator", fmt.Sprintf("failed to get portrait generator: %v", err)) + // do not requeue because it won't help return ctrl.Result{}, nil } @@ -190,14 +193,7 @@ func (r *HorizontalPortraitReconciler) SetupWithManager(mgr ctrl.Manager) error if !ok { return false } - switch hp.Spec.PortraitType { - case autoscalingv1alpha1.ReactivePortraitType: - case autoscalingv1alpha1.PredictivePortraitType: - case autoscalingv1alpha1.BurstPortraitType: - default: - return false - } - return true + return hp.Spec.ExternalGeneratorName == "" }), )). Watches(&source.Channel{Source: r.EventTrigger}, &handler.EnqueueRequestForObject{}).