diff --git a/api/v1/deployment_types.go b/api/v1/deployment_types.go index b1ddc3a..c660e56 100644 --- a/api/v1/deployment_types.go +++ b/api/v1/deployment_types.go @@ -20,7 +20,6 @@ type ApplicationSpec struct { // Ignored when HorizontalPodAutoscaler is configured. // +optional // +kubebuilder:validation:Minimum=0 - // +kubebuilder:default=1 Replicas *int32 `json:"replicas,omitempty"` // Resources specifies compute resource requirements. diff --git a/api/v1/logicflowruntime_webhook.go b/api/v1/logicflowruntime_webhook.go index 5e952e7..84f4e24 100644 --- a/api/v1/logicflowruntime_webhook.go +++ b/api/v1/logicflowruntime_webhook.go @@ -6,6 +6,25 @@ import ( "sigs.k8s.io/controller-runtime/pkg/webhook/admission" ) +// +kubebuilder:webhook:path=/mutate-logic-kubesmarts-org-v1-logicflowruntime,mutating=true,failurePolicy=fail,sideEffects=None,groups=logic.kubesmarts.org,resources=logicflowruntimes,verbs=create;update,versions=v1,name=mlogicflowruntime-v1.kb.io,admissionReviewVersions=v1 + +// +kubebuilder:object:generate=false +type LogicFlowRuntimeDefaulter struct{} + +var _ admission.Defaulter[*LogicFlowRuntime] = &LogicFlowRuntimeDefaulter{} + +// Default sets Replicas to 1 if not specified. +// We use a webhook instead of +kubebuilder:default=1 because ApplicationSpec is also used by LogicPlatform.DataIndex, +// which needs to support HPA management without operator enforcement. The webhook allows us to apply the default +// conditionally only for LogicFlowRuntime, leaving DataIndex.Application.Replicas nil for HPA to manage freely. +func (d *LogicFlowRuntimeDefaulter) Default(_ context.Context, obj *LogicFlowRuntime) error { + if obj.Spec.Replicas == nil { + one := int32(1) + obj.Spec.Replicas = &one + } + return nil +} + // +kubebuilder:webhook:path=/validate-logic-kubesmarts-org-v1-logicflowruntime,mutating=false,failurePolicy=fail,sideEffects=None,groups=logic.kubesmarts.org,resources=logicflowruntimes,verbs=create;update,versions=v1,name=vlogicflowruntime-v1.kb.io,admissionReviewVersions=v1 type LogicFlowRuntimeValidator struct{} diff --git a/api/v1/webhook_integration_test.go b/api/v1/webhook_integration_test.go index 7eba241..295f895 100644 --- a/api/v1/webhook_integration_test.go +++ b/api/v1/webhook_integration_test.go @@ -81,6 +81,7 @@ func TestWebhookIntegration(t *testing.T) { t.Fatalf("register LFD webhook: %v", err) } if err := builder.WebhookManagedBy(mgr, &LogicFlowRuntime{}). + WithDefaulter(&LogicFlowRuntimeDefaulter{}). WithValidator(&LogicFlowRuntimeValidator{}). Complete(); err != nil { t.Fatalf("register LFR webhook: %v", err) diff --git a/cmd/main.go b/cmd/main.go index 84bcc8c..709dbd1 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -248,6 +248,7 @@ func main() { os.Exit(1) } if err := builder.WebhookManagedBy(mgr, &logicv1.LogicFlowRuntime{}). + WithDefaulter(&logicv1.LogicFlowRuntimeDefaulter{}). WithValidator(&logicv1.LogicFlowRuntimeValidator{}). Complete(); err != nil { setupLog.Error(err, "unable to create webhook", "webhook", "LogicFlowRuntime") diff --git a/config/crd/bases/logic.kubesmarts.org_logicflowruntimes.yaml b/config/crd/bases/logic.kubesmarts.org_logicflowruntimes.yaml index 2745edc..3a11da6 100644 --- a/config/crd/bases/logic.kubesmarts.org_logicflowruntimes.yaml +++ b/config/crd/bases/logic.kubesmarts.org_logicflowruntimes.yaml @@ -9474,7 +9474,6 @@ spec: type: array type: object replicas: - default: 1 description: |- Replicas is the desired number of pod replicas. Ignored when HorizontalPodAutoscaler is configured. diff --git a/config/crd/bases/logic.kubesmarts.org_logicplatforms.yaml b/config/crd/bases/logic.kubesmarts.org_logicplatforms.yaml index 7697b98..06e66d5 100644 --- a/config/crd/bases/logic.kubesmarts.org_logicplatforms.yaml +++ b/config/crd/bases/logic.kubesmarts.org_logicplatforms.yaml @@ -9500,7 +9500,6 @@ spec: type: array type: object replicas: - default: 1 description: |- Replicas is the desired number of pod replicas. Ignored when HorizontalPodAutoscaler is configured. @@ -19276,7 +19275,6 @@ spec: type: array type: object replicas: - default: 1 description: |- Replicas is the desired number of pod replicas. Ignored when HorizontalPodAutoscaler is configured. @@ -28834,7 +28832,6 @@ spec: type: array type: object replicas: - default: 1 description: |- Replicas is the desired number of pod replicas. Ignored when HorizontalPodAutoscaler is configured. diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index 49087ab..6a39946 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -4,6 +4,26 @@ kind: MutatingWebhookConfiguration metadata: name: mutating-webhook-configuration webhooks: +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /mutate-logic-kubesmarts-org-v1-logicflowruntime + failurePolicy: Fail + name: mlogicflowruntime-v1.kb.io + rules: + - apiGroups: + - logic.kubesmarts.org + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + resources: + - logicflowruntimes + sideEffects: None - admissionReviewVersions: - v1 clientConfig: diff --git a/docs/antora/modules/ROOT/pages/deployment/production.adoc b/docs/antora/modules/ROOT/pages/deployment/production.adoc index 5d981d6..de4be79 100644 --- a/docs/antora/modules/ROOT/pages/deployment/production.adoc +++ b/docs/antora/modules/ROOT/pages/deployment/production.adoc @@ -90,13 +90,81 @@ spec: == Horizontal Pod Autoscaling -The operator sets `spec.replicas` on the managed `Deployment`. -Create an `HorizontalPodAutoscaler` targeting that `Deployment` to scale based on CPU or custom metrics. +The operator exposes the Kubernetes scale subresource on LogicFlowRuntime and does not enforce default replicas on LogicPlatform.DataIndex, enabling HPA management for both. + +=== LogicFlowRuntime Autoscaling + +Target LogicFlowRuntime directly (not the Deployment). +HPA updates `spec.replicas` on the CR, which the operator uses to reconcile both the Deployment AND lease pool. +If you target the Deployment directly, leases won't track replica changes. + +Example HPA for a LogicFlowRuntime named `hello-runtime`: + +[source,yaml] +---- +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: hello-runtime-autoscaler + namespace: default +spec: + scaleTargetRef: + apiVersion: logic.kubesmarts.org/v1 + kind: LogicFlowRuntime + name: hello-runtime + minReplicas: 1 + maxReplicas: 10 + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: 70 +---- [NOTE] ==== When PostgreSQL persistence is enabled, scale-up is safe — leases ensure each workflow instance is owned by exactly one pod. Without persistence, scale-up creates duplicate in-memory state for in-flight workflows. + +The operator manages lease assignment/cleanup automatically when replica count changes. +==== + +=== Data Index Autoscaling + +Target the generated Data Index Deployment directly (`flow-platform-data-index` or similar). +Leave `spec.dataIndex.application.replicas` unset in LogicPlatform so the operator doesn't enforce a value. + +Example HPA for Data Index: + +[source,yaml] +---- +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: data-index-autoscaler + namespace: data-index-system +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: flow-platform-data-index # Deployment created by operator + minReplicas: 1 + maxReplicas: 5 + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: 75 +---- + +[NOTE] +==== +Do not set `spec.dataIndex.application.replicas` when using HPA. +The operator respects the value if set, but will override HPA scaling. ==== == cert-manager