diff --git a/api/v1alpha1/envoyproxy_types.go b/api/v1alpha1/envoyproxy_types.go index e11da79d850..acf34417c76 100644 --- a/api/v1alpha1/envoyproxy_types.go +++ b/api/v1alpha1/envoyproxy_types.go @@ -134,7 +134,7 @@ type EnvoyProxyKubernetesProvider struct { // +optional // +kubebuilder:validation:XValidation:message="minReplicas must be greater than 0",rule="!has(self.minReplicas) || self.minReplicas > 0" // +kubebuilder:validation:XValidation:message="maxReplicas must be greater than 0",rule="!has(self.maxReplicas) || self.maxReplicas > 0" - // +kubebuilder:validation:XValidation:message="maxReplicas cannot be less than minReplicas",rule="!has(self.minReplicas) || self.maxReplicas >= self.minReplicas" + // +kubebuilder:validation:XValidation:message="maxReplicas cannot be less than or equal to minReplicas",rule="!has(self.minReplicas) || self.maxReplicas > self.minReplicas" EnvoyHpa *KubernetesHorizontalPodAutoscalerSpec `json:"envoyHpa,omitempty"` } diff --git a/api/v1alpha1/shared_types.go b/api/v1alpha1/shared_types.go index bcc406dba69..e6d19f960ae 100644 --- a/api/v1alpha1/shared_types.go +++ b/api/v1alpha1/shared_types.go @@ -278,20 +278,18 @@ const ( ) // KubernetesHorizontalPodAutoscalerSpec defines Kubernetes Horizontal Pod Autoscaler settings of Envoy Proxy Deployment +// See k8s.io.autoscaling.v2.HorizontalPodAutoScalerSpec type KubernetesHorizontalPodAutoscalerSpec struct { // minReplicas is the lower limit for the number of replicas to which the autoscaler // can scale down. It defaults to 1 replica. - // See k8s.io.autoscaling.v2.HorizontalPodAutoScalerSpec // // +optional MinReplicas *int32 `json:"minReplicas,omitempty"` // maxReplicas is the upper limit for the number of replicas to which the autoscaler can scale up. - // It cannot be less that minReplicas. It defaults to 1 replica. - // See k8s.io.autoscaling.v2.HorizontalPodAutoScalerSpec + // It cannot be less that minReplicas. // - // +optional - MaxReplicas *int32 `json:"maxReplicas,omitempty"` + MaxReplicas *int32 `json:"maxReplicas"` // metrics contains the specifications for which to use to calculate the // desired replica count (the maximum replica count across all metrics will @@ -299,8 +297,6 @@ type KubernetesHorizontalPodAutoscalerSpec struct { // If left empty, it defaults to being based on CPU utilization with average on 80% usage. // // +optional - // - // See k8s.io.autoscaling.v2.HorizontalPodAutoScalerBehavior. Metrics []autoscalingv2.MetricSpec `json:"metrics,omitempty"` // behavior configures the scaling behavior of the target diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml index d0bd299aacc..7bff9e4149a 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml @@ -5366,16 +5366,15 @@ spec: maxReplicas: description: maxReplicas is the upper limit for the number of replicas to which the autoscaler can scale up. It - cannot be less that minReplicas. It defaults to 1 replica. - See k8s.io.autoscaling.v2.HorizontalPodAutoScalerSpec + cannot be less that minReplicas. format: int32 type: integer metrics: - description: "metrics contains the specifications for - which to use to calculate the desired replica count - (the maximum replica count across all metrics will be - used). If left empty, it defaults to being based on - CPU utilization with average on 80% usage. \n See k8s.io.autoscaling.v2.HorizontalPodAutoScalerBehavior." + description: metrics contains the specifications for which + to use to calculate the desired replica count (the maximum + replica count across all metrics will be used). If left + empty, it defaults to being based on CPU utilization + with average on 80% usage. items: description: MetricSpec specifies how to scale based on a single metric (only `type` and one other matching @@ -5902,17 +5901,19 @@ spec: minReplicas: description: minReplicas is the lower limit for the number of replicas to which the autoscaler can scale down. - It defaults to 1 replica. See k8s.io.autoscaling.v2.HorizontalPodAutoScalerSpec + It defaults to 1 replica. format: int32 type: integer + required: + - maxReplicas type: object x-kubernetes-validations: - message: minReplicas must be greater than 0 rule: '!has(self.minReplicas) || self.minReplicas > 0' - message: maxReplicas must be greater than 0 rule: '!has(self.maxReplicas) || self.maxReplicas > 0' - - message: maxReplicas cannot be less than minReplicas - rule: '!has(self.minReplicas) || self.maxReplicas >= self.minReplicas' + - message: maxReplicas cannot be less than or equal to minReplicas + rule: '!has(self.minReplicas) || self.maxReplicas > self.minReplicas' envoyService: description: EnvoyService defines the desired state of the Envoy service resource. If unspecified, default settings diff --git a/internal/infrastructure/kubernetes/infra_resource.go b/internal/infrastructure/kubernetes/infra_resource.go index 1f4cf5ff5b4..248cbe11af4 100644 --- a/internal/infrastructure/kubernetes/infra_resource.go +++ b/internal/infrastructure/kubernetes/infra_resource.go @@ -9,15 +9,14 @@ import ( "context" "reflect" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" appsv1 "k8s.io/api/apps/v1" autoscalingv2 "k8s.io/api/autoscaling/v2" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" - "github.com/envoyproxy/gateway/internal/infrastructure/kubernetes/resource" ) diff --git a/internal/infrastructure/kubernetes/proxy/resource_provider_test.go b/internal/infrastructure/kubernetes/proxy/resource_provider_test.go index f1b295475f8..f4603fc370c 100644 --- a/internal/infrastructure/kubernetes/proxy/resource_provider_test.go +++ b/internal/infrastructure/kubernetes/proxy/resource_provider_test.go @@ -16,7 +16,6 @@ import ( appsv1 "k8s.io/api/apps/v1" autoscalingv2 "k8s.io/api/autoscaling/v2" corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/resource" "k8s.io/utils/pointer" "sigs.k8s.io/yaml" @@ -523,7 +522,9 @@ func TestHorizontalPodAutoscaler(t *testing.T) { { caseName: "default", infra: newTestInfra(), - hpa: &egv1a1.KubernetesHorizontalPodAutoscalerSpec{}, + hpa: &egv1a1.KubernetesHorizontalPodAutoscalerSpec{ + MaxReplicas: ptr.To[int32](1), + }, }, { caseName: "custom", diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md index 5efbbe8a865..4fa146d29a9 100644 --- a/site/content/en/latest/api/extension_types.md +++ b/site/content/en/latest/api/extension_types.md @@ -981,17 +981,16 @@ _Appears in:_ -KubernetesHorizontalPodAutoscalerSpec defines Kubernetes Horizontal Pod Autoscaler settings of Envoy Proxy Deployment +KubernetesHorizontalPodAutoscalerSpec defines Kubernetes Horizontal Pod Autoscaler settings of Envoy Proxy Deployment See k8s.io.autoscaling.v2.HorizontalPodAutoScalerSpec _Appears in:_ - [EnvoyProxyKubernetesProvider](#envoyproxykubernetesprovider) | Field | Description | | --- | --- | -| `minReplicas` _integer_ | minReplicas is the lower limit for the number of replicas to which the autoscaler can scale down. It defaults to 1 replica. See k8s.io.autoscaling.v2.HorizontalPodAutoScalerSpec | -| `maxReplicas` _integer_ | maxReplicas is the upper limit for the number of replicas to which the autoscaler can scale up. It cannot be less that minReplicas. It defaults to 1 replica. See k8s.io.autoscaling.v2.HorizontalPodAutoScalerSpec | -| `metrics` _[MetricSpec](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#metricspec-v2-autoscaling) array_ | metrics contains the specifications for which to use to calculate the desired replica count (the maximum replica count across all metrics will be used). If left empty, it defaults to being based on CPU utilization with average on 80% usage. - See k8s.io.autoscaling.v2.HorizontalPodAutoScalerBehavior. | +| `minReplicas` _integer_ | minReplicas is the lower limit for the number of replicas to which the autoscaler can scale down. It defaults to 1 replica. | +| `maxReplicas` _integer_ | maxReplicas is the upper limit for the number of replicas to which the autoscaler can scale up. It cannot be less that minReplicas. | +| `metrics` _[MetricSpec](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#metricspec-v2-autoscaling) array_ | metrics contains the specifications for which to use to calculate the desired replica count (the maximum replica count across all metrics will be used). If left empty, it defaults to being based on CPU utilization with average on 80% usage. | | `behavior` _[HorizontalPodAutoscalerBehavior](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#horizontalpodautoscalerbehavior-v2-autoscaling)_ | behavior configures the scaling behavior of the target in both Up and Down directions (scaleUp and scaleDown fields respectively). If not set, the default HPAScalingRules for scale up and scale down are used. See k8s.io.autoscaling.v2.HorizontalPodAutoScalerBehavior. | diff --git a/test/cel-validation/envoyproxy_test.go b/test/cel-validation/envoyproxy_test.go index ac949ff971d..86b5a8ddc5e 100644 --- a/test/cel-validation/envoyproxy_test.go +++ b/test/cel-validation/envoyproxy_test.go @@ -419,6 +419,86 @@ func TestEnvoyProxyProvider(t *testing.T) { }, wantErrors: []string{}, }, + { + desc: "ProxyHpa-maxReplicas-is-required", + mutate: func(envoy *egv1a1.EnvoyProxy) { + envoy.Spec = egv1a1.EnvoyProxySpec{ + Provider: &egv1a1.EnvoyProxyProvider{ + Type: egv1a1.ProviderTypeKubernetes, + Kubernetes: &egv1a1.EnvoyProxyKubernetesProvider{ + EnvoyHpa: &egv1a1.KubernetesHorizontalPodAutoscalerSpec{}, + }, + }, + } + }, + wantErrors: []string{"spec.provider.kubernetes.envoyHpa.maxReplicas: Required value"}, + }, + { + desc: "ProxyHpa-minReplicas-less-than-0", + mutate: func(envoy *egv1a1.EnvoyProxy) { + envoy.Spec = egv1a1.EnvoyProxySpec{ + Provider: &egv1a1.EnvoyProxyProvider{ + Type: egv1a1.ProviderTypeKubernetes, + Kubernetes: &egv1a1.EnvoyProxyKubernetesProvider{ + EnvoyHpa: &egv1a1.KubernetesHorizontalPodAutoscalerSpec{ + MinReplicas: ptr.To[int32](-1), + MaxReplicas: ptr.To[int32](2), + }, + }, + }, + } + }, + wantErrors: []string{"minReplicas must be greater than 0"}, + }, + { + desc: "ProxyHpa-maxReplicas-less-than-0", + mutate: func(envoy *egv1a1.EnvoyProxy) { + envoy.Spec = egv1a1.EnvoyProxySpec{ + Provider: &egv1a1.EnvoyProxyProvider{ + Type: egv1a1.ProviderTypeKubernetes, + Kubernetes: &egv1a1.EnvoyProxyKubernetesProvider{ + EnvoyHpa: &egv1a1.KubernetesHorizontalPodAutoscalerSpec{ + MaxReplicas: ptr.To[int32](-1), + }, + }, + }, + } + }, + wantErrors: []string{"maxReplicas must be greater than 0"}, + }, + { + desc: "ProxyHpa-maxReplicas-less-than-minReplicas", + mutate: func(envoy *egv1a1.EnvoyProxy) { + envoy.Spec = egv1a1.EnvoyProxySpec{ + Provider: &egv1a1.EnvoyProxyProvider{ + Type: egv1a1.ProviderTypeKubernetes, + Kubernetes: &egv1a1.EnvoyProxyKubernetesProvider{ + EnvoyHpa: &egv1a1.KubernetesHorizontalPodAutoscalerSpec{ + MinReplicas: ptr.To[int32](5), + MaxReplicas: ptr.To[int32](2), + }, + }, + }, + } + }, + wantErrors: []string{"maxReplicas cannot be less than or equal to minReplicas"}, + }, + { + desc: "ProxyHpa-valid", + mutate: func(envoy *egv1a1.EnvoyProxy) { + envoy.Spec = egv1a1.EnvoyProxySpec{ + Provider: &egv1a1.EnvoyProxyProvider{ + Type: egv1a1.ProviderTypeKubernetes, + Kubernetes: &egv1a1.EnvoyProxyKubernetesProvider{ + EnvoyHpa: &egv1a1.KubernetesHorizontalPodAutoscalerSpec{ + MinReplicas: ptr.To[int32](5), + MaxReplicas: ptr.To[int32](10), + }, + }, + }, + } + }, + }, } for _, tc := range cases {