From 1efa39b0ce533e4143fd8fc728b0cab970f22e65 Mon Sep 17 00:00:00 2001 From: Andrii Chubatiuk Date: Mon, 16 Mar 2026 14:11:51 +0200 Subject: [PATCH 01/22] reconcile: wait till PVC is in bound phase --- docs/env.md | 5 +- internal/config/config.go | 14 ++- .../operator/factory/k8stools/interceptors.go | 100 +++++++----------- .../operator/factory/reconcile/daemonset.go | 2 +- .../operator/factory/reconcile/deploy.go | 2 +- .../operator/factory/reconcile/pvc.go | 62 +++++++++-- .../operator/factory/reconcile/pvc_test.go | 7 +- .../operator/factory/reconcile/reconcile.go | 27 +++-- .../operator/factory/reconcile/statefulset.go | 12 +-- .../reconcile/statefulset_pvc_expand.go | 7 ++ .../reconcile/statefulset_pvc_expand_test.go | 72 +++++++++++-- .../operator/factory/reconcile/vmagent.go | 2 +- .../operator/factory/reconcile/vmauth.go | 2 +- .../operator/factory/reconcile/vmcluster.go | 2 +- internal/manager/manager.go | 2 +- 15 files changed, 207 insertions(+), 111 deletions(-) diff --git a/docs/env.md b/docs/env.md index b54ea6780..d682c4195 100644 --- a/docs/env.md +++ b/docs/env.md @@ -230,7 +230,10 @@ | VM_FILTERPROMETHEUSCONVERTERANNOTATIONPREFIXES: `-` #
allows filtering for converted annotations, annotations with matched prefix will be ignored | | VM_CLUSTERDOMAINNAME: `-` #
Defines domain name suffix for in-cluster addresses most known ClusterDomainName is .cluster.local | | VM_APPREADYTIMEOUT: `80s` #
Defines deadline for deployment/statefulset to transit into ready state to wait for transition to ready state | -| VM_PODWAITREADYTIMEOUT: `80s` #
Defines single pod deadline to wait for transition to ready state | | VM_PODWAITREADYINTERVALCHECK: `5s` #
Defines poll interval for pods ready check at statefulset rollout update | +| VM_PODWAITREADYTIMEOUT: `80s` #
Defines single pod deadline to wait for transition to ready state | +| VM_PVC_WAIT_READY_INTERVAL: `5s` #
Defines poll interval for PVC ready check | +| VM_PVC_WAIT_READY_TIMEOUT: `80s` #
Defines poll timeout for PVC ready check | +| VM_WAIT_READY_INTERVAL: `5s` #
Defines poll interval for VM CRs | | VM_FORCERESYNCINTERVAL: `60s` #
configures force resync interval for VMAgent, VMAlert, VMAlertmanager and VMAuth. | | VM_ENABLESTRICTSECURITY: `false` #
EnableStrictSecurity will add default `securityContext` to pods and containers created by operator Default PodSecurityContext include: 1. RunAsNonRoot: true 2. RunAsUser/RunAsGroup/FSGroup: 65534 '65534' refers to 'nobody' in all the used default images like alpine, busybox. If you're using customize image, please make sure '65534' is a valid uid in there or specify SecurityContext. 3. FSGroupChangePolicy: &onRootMismatch If KubeVersion>=1.20, use `FSGroupChangePolicy="onRootMismatch"` to skip the recursive permission change when the root of the volume already has the correct permissions 4. SeccompProfile: type: RuntimeDefault Use `RuntimeDefault` seccomp profile by default, which is defined by the container runtime, instead of using the Unconfined (seccomp disabled) mode. Default container SecurityContext include: 1. AllowPrivilegeEscalation: false 2. ReadOnlyRootFilesystem: true 3. Capabilities: drop: - all turn off `EnableStrictSecurity` by default, see https://github.com/VictoriaMetrics/operator/issues/749 for details | diff --git a/internal/config/config.go b/internal/config/config.go index 2de9d0bf4..2343fe37b 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -546,13 +546,19 @@ type BaseOperatorConf struct { // Defines deadline for deployment/statefulset // to transit into ready state // to wait for transition to ready state - AppReadyTimeout time.Duration `default:"80s" env:"VM_APPREADYTIMEOUT"` + AppWaitReadyTimeout time.Duration `default:"80s" env:"VM_APPREADYTIMEOUT"` + // Defines poll interval for pods ready check + // at statefulset rollout update + PodWaitReadyInterval time.Duration `default:"5s" env:"VM_PODWAITREADYINTERVALCHECK"` // Defines single pod deadline // to wait for transition to ready state PodWaitReadyTimeout time.Duration `default:"80s" env:"VM_PODWAITREADYTIMEOUT"` - // Defines poll interval for pods ready check - // at statefulset rollout update - PodWaitReadyIntervalCheck time.Duration `default:"5s" env:"VM_PODWAITREADYINTERVALCHECK"` + // Defines poll interval for PVC ready check + PVCWaitReadyInterval time.Duration `default:"5s" env:"VM_PVC_WAIT_READY_INTERVAL"` + // Defines poll timeout for PVC ready check + PVCWaitReadyTimeout time.Duration `default:"80s" env:"VM_PVC_WAIT_READY_TIMEOUT"` + // Defines poll interval for VM CRs + VMWaitReadyInterval time.Duration `default:"5s" env:"VM_WAIT_READY_INTERVAL"` // configures force resync interval for VMAgent, VMAlert, VMAlertmanager and VMAuth. ForceResyncInterval time.Duration `default:"60s" env:"VM_FORCERESYNCINTERVAL"` // EnableStrictSecurity will add default `securityContext` to pods and containers created by operator diff --git a/internal/controller/operator/factory/k8stools/interceptors.go b/internal/controller/operator/factory/k8stools/interceptors.go index 6081fa48d..f8d719825 100644 --- a/internal/controller/operator/factory/k8stools/interceptors.go +++ b/internal/controller/operator/factory/k8stools/interceptors.go @@ -4,6 +4,7 @@ import ( "context" appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/apiutil" @@ -12,80 +13,55 @@ import ( vmv1beta1 "github.com/VictoriaMetrics/operator/api/operator/v1beta1" ) +func updateStatus(ctx context.Context, cl client.WithWatch, obj client.Object) error { + switch v := obj.(type) { + case *appsv1.StatefulSet: + v.Status.ObservedGeneration = v.Generation + v.Status.ReadyReplicas = ptr.Deref(v.Spec.Replicas, 0) + v.Status.UpdatedReplicas = ptr.Deref(v.Spec.Replicas, 0) + v.Status.CurrentReplicas = ptr.Deref(v.Spec.Replicas, 0) + v.Status.UpdateRevision = "v1" + v.Status.CurrentRevision = "v1" + case *appsv1.Deployment: + v.Status.ObservedGeneration = v.Generation + v.Status.Conditions = append(v.Status.Conditions, appsv1.DeploymentCondition{ + Type: appsv1.DeploymentProgressing, + Reason: "NewReplicaSetAvailable", + Status: "True", + }) + v.Status.UpdatedReplicas = ptr.Deref(v.Spec.Replicas, 0) + v.Status.ReadyReplicas = ptr.Deref(v.Spec.Replicas, 0) + case *vmv1beta1.VMAgent: + v.Status.UpdateStatus = vmv1beta1.UpdateStatusOperational + v.Status.ObservedGeneration = v.Generation + case *vmv1beta1.VMCluster: + v.Status.UpdateStatus = vmv1beta1.UpdateStatusOperational + v.Status.ObservedGeneration = v.Generation + case *vmv1beta1.VMAuth: + v.Status.UpdateStatus = vmv1beta1.UpdateStatusOperational + v.Status.ObservedGeneration = v.Generation + case *corev1.PersistentVolumeClaim: + v.Status.Capacity = v.Spec.Resources.Requests + default: + return nil + } + return cl.Status().Update(ctx, obj) +} + // GetInterceptorsWithObjects returns interceptors for objects func GetInterceptorsWithObjects() interceptor.Funcs { return interceptor.Funcs{ Create: func(ctx context.Context, cl client.WithWatch, obj client.Object, opts ...client.CreateOption) error { - switch v := obj.(type) { - case *appsv1.StatefulSet: - v.Status.ObservedGeneration = v.Generation - v.Status.ReadyReplicas = ptr.Deref(v.Spec.Replicas, 0) - v.Status.UpdatedReplicas = ptr.Deref(v.Spec.Replicas, 0) - v.Status.CurrentReplicas = ptr.Deref(v.Spec.Replicas, 0) - v.Status.UpdateRevision = "v1" - v.Status.CurrentRevision = "v1" - case *appsv1.Deployment: - v.Status.ObservedGeneration = v.Generation - v.Status.Conditions = append(v.Status.Conditions, appsv1.DeploymentCondition{ - Type: appsv1.DeploymentProgressing, - Reason: "NewReplicaSetAvailable", - Status: "True", - }) - v.Status.UpdatedReplicas = ptr.Deref(v.Spec.Replicas, 0) - v.Status.ReadyReplicas = ptr.Deref(v.Spec.Replicas, 0) - } if err := cl.Create(ctx, obj, opts...); err != nil { return err } - switch v := obj.(type) { - case *vmv1beta1.VMAgent: - v.Status.UpdateStatus = vmv1beta1.UpdateStatusOperational - v.Status.ObservedGeneration = v.Generation - return cl.Status().Update(ctx, v) - case *vmv1beta1.VMCluster: - v.Status.UpdateStatus = vmv1beta1.UpdateStatusOperational - v.Status.ObservedGeneration = v.Generation - return cl.Status().Update(ctx, v) - case *vmv1beta1.VMAuth: - v.Status.UpdateStatus = vmv1beta1.UpdateStatusOperational - v.Status.ObservedGeneration = v.Generation - return cl.Status().Update(ctx, v) - } - return nil + return updateStatus(ctx, cl, obj) }, Update: func(ctx context.Context, cl client.WithWatch, obj client.Object, opts ...client.UpdateOption) error { - switch v := obj.(type) { - case *appsv1.StatefulSet: - v.Status.ObservedGeneration = v.Generation - v.Status.ReadyReplicas = ptr.Deref(v.Spec.Replicas, 0) - v.Status.UpdatedReplicas = ptr.Deref(v.Spec.Replicas, 0) - v.Status.CurrentReplicas = ptr.Deref(v.Spec.Replicas, 0) - v.Status.UpdateRevision = "v1" - v.Status.CurrentRevision = "v1" - case *appsv1.Deployment: - v.Status.ObservedGeneration = v.Generation - v.Status.UpdatedReplicas = ptr.Deref(v.Spec.Replicas, 0) - v.Status.ReadyReplicas = ptr.Deref(v.Spec.Replicas, 0) - v.Status.Replicas = ptr.Deref(v.Spec.Replicas, 0) - } if err := cl.Update(ctx, obj, opts...); err != nil { return err } - switch v := obj.(type) { - case *vmv1beta1.VMAgent: - v.Status.UpdateStatus = vmv1beta1.UpdateStatusOperational - v.Status.ObservedGeneration = v.Generation - return cl.Status().Update(ctx, v) - case *vmv1beta1.VMCluster: - v.Status.UpdateStatus = vmv1beta1.UpdateStatusOperational - v.Status.ObservedGeneration = v.Generation - return cl.Status().Update(ctx, v) - case *vmv1beta1.VMAuth: - v.Status.UpdateStatus = vmv1beta1.UpdateStatusOperational - v.Status.ObservedGeneration = v.Generation - return cl.Status().Update(ctx, v) - } - return nil + return updateStatus(ctx, cl, obj) }, } } diff --git a/internal/controller/operator/factory/reconcile/daemonset.go b/internal/controller/operator/factory/reconcile/daemonset.go index 9a26417d4..6bb87f550 100644 --- a/internal/controller/operator/factory/reconcile/daemonset.go +++ b/internal/controller/operator/factory/reconcile/daemonset.go @@ -67,7 +67,7 @@ func DaemonSet(ctx context.Context, rclient client.Client, newObj, prevObj *apps if err != nil { return err } - return waitDaemonSetReady(ctx, rclient, newObj, appWaitReadyDeadline) + return waitDaemonSetReady(ctx, rclient, newObj, appWaitReadyTimeout) } // waitDeploymentReady waits until deployment's replicaSet rollouts and all new pods is ready diff --git a/internal/controller/operator/factory/reconcile/deploy.go b/internal/controller/operator/factory/reconcile/deploy.go index 4a3140f57..5a2cd681a 100644 --- a/internal/controller/operator/factory/reconcile/deploy.go +++ b/internal/controller/operator/factory/reconcile/deploy.go @@ -69,7 +69,7 @@ func Deployment(ctx context.Context, rclient client.Client, newObj, prevObj *app if err != nil { return err } - return waitForDeploymentReady(ctx, rclient, newObj, appWaitReadyDeadline) + return waitForDeploymentReady(ctx, rclient, newObj, appWaitReadyTimeout) } // waitForDeploymentReady waits until deployment's replicaSet rollouts and all new pods is ready diff --git a/internal/controller/operator/factory/reconcile/pvc.go b/internal/controller/operator/factory/reconcile/pvc.go index 0d42b1a0d..ac88f37aa 100644 --- a/internal/controller/operator/factory/reconcile/pvc.go +++ b/internal/controller/operator/factory/reconcile/pvc.go @@ -6,8 +6,10 @@ import ( corev1 "k8s.io/api/core/v1" k8serrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/wait" "sigs.k8s.io/controller-runtime/pkg/client" "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/logger" @@ -20,24 +22,62 @@ import ( // in case of deletion timestamp > 0 does nothing // user must manually remove finalizer if needed func PersistentVolumeClaim(ctx context.Context, rclient client.Client, newObj, prevObj *corev1.PersistentVolumeClaim, owner *metav1.OwnerReference) error { - l := logger.WithContext(ctx) - var existingObj corev1.PersistentVolumeClaim nsn := types.NamespacedName{Namespace: newObj.Namespace, Name: newObj.Name} - if err := rclient.Get(ctx, nsn, &existingObj); err != nil { - if k8serrors.IsNotFound(err) { - l.Info(fmt.Sprintf("creating new PVC=%s", nsn.String())) - if err := rclient.Create(ctx, newObj); err != nil { - return fmt.Errorf("cannot create new PVC=%s: %w", nsn.String(), err) + var existingObj corev1.PersistentVolumeClaim + err := retryOnConflict(func() error { + if err := rclient.Get(ctx, nsn, &existingObj); err != nil { + if k8serrors.IsNotFound(err) { + logger.WithContext(ctx).Info(fmt.Sprintf("creating new PVC=%s", nsn.String())) + return rclient.Create(ctx, newObj) } + return fmt.Errorf("cannot get existing PVC=%s: %w", nsn.String(), err) + } + if !existingObj.DeletionTimestamp.IsZero() { return nil } - return fmt.Errorf("cannot get existing PVC=%s: %w", nsn.String(), err) + return updatePVC(ctx, rclient, &existingObj, newObj, prevObj, owner) + }) + if err != nil { + return err + } + size := newObj.Spec.Resources.Requests[corev1.ResourceStorage] + if !existingObj.CreationTimestamp.IsZero() { + size = existingObj.Spec.Resources.Requests[corev1.ResourceStorage] + } + if err = waitForPVCReady(ctx, rclient, nsn, size); err != nil { + return err } if !existingObj.DeletionTimestamp.IsZero() { - l.Info(fmt.Sprintf("PVC=%s has non zero DeletionTimestamp, skip update."+ + logger.WithContext(ctx).Info(fmt.Sprintf("PVC=%s has non zero DeletionTimestamp, skip update."+ " To fix this, make backup for this pvc, delete pvc finalizers and restore from backup.", nsn.String())) - return nil } + return nil +} - return updatePVC(ctx, rclient, &existingObj, newObj, prevObj, owner) +func waitForPVCReady(ctx context.Context, rclient client.Client, nsn types.NamespacedName, size resource.Quantity) error { + var pvc corev1.PersistentVolumeClaim + return wait.PollUntilContextTimeout(ctx, pvcWaitReadyInterval, pvcWaitReadyTimeout, true, func(ctx context.Context) (done bool, err error) { + if err := rclient.Get(ctx, nsn, &pvc); err != nil { + if k8serrors.IsNotFound(err) { + return false, nil + } + return false, fmt.Errorf("cannot get PVC=%s: %w", nsn.String(), err) + } + if !pvc.DeletionTimestamp.IsZero() { + return true, nil + } + if len(pvc.Status.Capacity) == 0 { + return true, nil + } + actualSize := pvc.Status.Capacity[corev1.ResourceStorage] + if actualSize.Cmp(size) < 0 { + return false, nil + } + for _, condition := range pvc.Status.Conditions { + if condition.Type == corev1.PersistentVolumeClaimResizing && condition.Status == corev1.ConditionTrue { + return false, nil + } + } + return true, nil + }) } diff --git a/internal/controller/operator/factory/reconcile/pvc_test.go b/internal/controller/operator/factory/reconcile/pvc_test.go index 4f20a5279..c44a0e8c5 100644 --- a/internal/controller/operator/factory/reconcile/pvc_test.go +++ b/internal/controller/operator/factory/reconcile/pvc_test.go @@ -38,6 +38,7 @@ func TestPersistentVolumeClaimReconcile(t *testing.T) { }, }, } + pvc.Status.Capacity = pvc.Spec.Resources.Requests for _, fn := range fns { fn(pvc) } @@ -47,7 +48,7 @@ func TestPersistentVolumeClaimReconcile(t *testing.T) { f := func(o opts) { t.Helper() ctx := context.Background() - cl := k8stools.GetTestClientWithActions(o.predefinedObjects) + cl := k8stools.GetTestClientWithActionsAndObjects(o.predefinedObjects) synctest.Test(t, func(t *testing.T) { assert.NoError(t, PersistentVolumeClaim(ctx, cl, o.new, o.prev, nil)) assert.Equal(t, o.actions, cl.Actions) @@ -62,6 +63,7 @@ func TestPersistentVolumeClaimReconcile(t *testing.T) { actions: []k8stools.ClientAction{ {Verb: "Get", Kind: "PersistentVolumeClaim", Resource: nn}, {Verb: "Create", Kind: "PersistentVolumeClaim", Resource: nn}, + {Verb: "Get", Kind: "PersistentVolumeClaim", Resource: nn}, }, }) @@ -74,6 +76,7 @@ func TestPersistentVolumeClaimReconcile(t *testing.T) { }, actions: []k8stools.ClientAction{ {Verb: "Get", Kind: "PersistentVolumeClaim", Resource: nn}, + {Verb: "Get", Kind: "PersistentVolumeClaim", Resource: nn}, }, }) @@ -89,6 +92,7 @@ func TestPersistentVolumeClaimReconcile(t *testing.T) { actions: []k8stools.ClientAction{ {Verb: "Get", Kind: "PersistentVolumeClaim", Resource: nn}, {Verb: "Update", Kind: "PersistentVolumeClaim", Resource: nn}, + {Verb: "Get", Kind: "PersistentVolumeClaim", Resource: nn}, }, }) @@ -106,6 +110,7 @@ func TestPersistentVolumeClaimReconcile(t *testing.T) { actions: []k8stools.ClientAction{ {Verb: "Get", Kind: "PersistentVolumeClaim", Resource: nn}, {Verb: "Update", Kind: "PersistentVolumeClaim", Resource: nn}, + {Verb: "Get", Kind: "PersistentVolumeClaim", Resource: nn}, }, }) } diff --git a/internal/controller/operator/factory/reconcile/reconcile.go b/internal/controller/operator/factory/reconcile/reconcile.go index bd39a4b67..1f0dccfb4 100644 --- a/internal/controller/operator/factory/reconcile/reconcile.go +++ b/internal/controller/operator/factory/reconcile/reconcile.go @@ -18,23 +18,32 @@ import ( "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" vmv1beta1 "github.com/VictoriaMetrics/operator/api/operator/v1beta1" + "github.com/VictoriaMetrics/operator/internal/config" "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/finalize" "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/logger" ) var ( - podWaitReadyIntervalCheck = 50 * time.Millisecond - appWaitReadyDeadline = 5 * time.Second - podWaitReadyTimeout = 5 * time.Second - vmStatusInterval = 5 * time.Second + pvcWaitReadyInterval = 1 * time.Second + pvcWaitReadyTimeout = 5 * time.Second + + podWaitReadyInterval = 1 * time.Second + podWaitReadyTimeout = 5 * time.Second + + appWaitReadyTimeout = 5 * time.Second + vmWaitReadyInterval = 5 * time.Second ) // Init sets package defaults -func Init(intervalCheck, appWaitDeadline, podReadyDeadline, statusInterval, statusUpdate time.Duration) { - podWaitReadyIntervalCheck = intervalCheck - appWaitReadyDeadline = appWaitDeadline - podWaitReadyTimeout = podReadyDeadline - vmStatusInterval = statusInterval +func Init(cfg *config.BaseOperatorConf, statusUpdate time.Duration) { + podWaitReadyInterval = cfg.PodWaitReadyInterval + podWaitReadyTimeout = cfg.PodWaitReadyTimeout + + pvcWaitReadyInterval = cfg.PVCWaitReadyInterval + pvcWaitReadyTimeout = cfg.PVCWaitReadyTimeout + + appWaitReadyTimeout = cfg.AppWaitReadyTimeout + vmWaitReadyInterval = cfg.VMWaitReadyInterval statusUpdateTTL = statusUpdate } diff --git a/internal/controller/operator/factory/reconcile/statefulset.go b/internal/controller/operator/factory/reconcile/statefulset.go index ebcc679d3..d0115d35a 100644 --- a/internal/controller/operator/factory/reconcile/statefulset.go +++ b/internal/controller/operator/factory/reconcile/statefulset.go @@ -43,7 +43,7 @@ func waitForStatefulSetReady(ctx context.Context, rclient client.Client, newObj if newObj.Spec.Replicas == nil { return nil } - err := wait.PollUntilContextTimeout(ctx, podWaitReadyIntervalCheck, appWaitReadyDeadline, true, func(ctx context.Context) (done bool, err error) { + err := wait.PollUntilContextTimeout(ctx, podWaitReadyInterval, appWaitReadyTimeout, true, func(ctx context.Context) (done bool, err error) { var existingObj appsv1.StatefulSet if err := rclient.Get(ctx, types.NamespacedName{Namespace: newObj.Namespace, Name: newObj.Name}, &existingObj); err != nil { if k8serrors.IsNotFound(err) { @@ -195,8 +195,8 @@ func StatefulSet(ctx context.Context, rclient client.Client, cr STSOptions, newO // if ObservedGeneration matches current generation func getLatestStsState(ctx context.Context, rclient client.Client, targetSTS types.NamespacedName) (*appsv1.StatefulSet, error) { var sts appsv1.StatefulSet - err := wait.PollUntilContextTimeout(ctx, podWaitReadyIntervalCheck, - appWaitReadyDeadline, true, func(ctx context.Context) (done bool, err error) { + err := wait.PollUntilContextTimeout(ctx, podWaitReadyInterval, + appWaitReadyTimeout, true, func(ctx context.Context) (done bool, err error) { if err := rclient.Get(ctx, targetSTS, &sts); err != nil { return true, err } @@ -227,7 +227,7 @@ type rollingUpdateOpts struct { // we always check if sts.Status.CurrentRevision needs update, to keep it equal to UpdateRevision // see https://github.com/kubernetes/kube-state-metrics/issues/1324#issuecomment-1779751992 func performRollingUpdateOnSts(ctx context.Context, rclient client.Client, obj *appsv1.StatefulSet, o rollingUpdateOpts) error { - time.Sleep(podWaitReadyIntervalCheck) + time.Sleep(podWaitReadyInterval) nsn := types.NamespacedName{ Name: obj.Name, Namespace: obj.Namespace, @@ -316,7 +316,7 @@ func performRollingUpdateOnSts(ctx context.Context, rclient client.Client, obj * l.Info(fmt.Sprintf("updating pod=%s revision label=%q", pod.Name, pod.Labels[podRevisionLabel])) // eviction may fail due to podDisruption budget and it's unexpected // so retry pod eviction - evictErr := wait.PollUntilContextTimeout(ctx, podWaitReadyIntervalCheck, podWaitReadyTimeout, true, func(ctx context.Context) (done bool, err error) { + evictErr := wait.PollUntilContextTimeout(ctx, podWaitReadyInterval, podWaitReadyTimeout, true, func(ctx context.Context) (done bool, err error) { if o.delete { if err := rclient.Delete(ctx, &pod); err != nil { if k8serrors.IsNotFound(err) { @@ -379,7 +379,7 @@ func PodIsReady(pod *corev1.Pod, minReadySeconds int32) bool { func waitForPodReady(ctx context.Context, rclient client.Client, nsn types.NamespacedName, desiredRevision string, minReadySeconds int32) error { var pod corev1.Pod - if err := wait.PollUntilContextTimeout(ctx, podWaitReadyIntervalCheck, podWaitReadyTimeout, true, func(ctx context.Context) (done bool, err error) { + if err := wait.PollUntilContextTimeout(ctx, podWaitReadyInterval, podWaitReadyTimeout, true, func(ctx context.Context) (done bool, err error) { if err := rclient.Get(ctx, nsn, &pod); err != nil { if k8serrors.IsNotFound(err) { return false, nil diff --git a/internal/controller/operator/factory/reconcile/statefulset_pvc_expand.go b/internal/controller/operator/factory/reconcile/statefulset_pvc_expand.go index a438b12cb..08b669ea7 100644 --- a/internal/controller/operator/factory/reconcile/statefulset_pvc_expand.go +++ b/internal/controller/operator/factory/reconcile/statefulset_pvc_expand.go @@ -122,6 +122,13 @@ func updateSTSPVC(ctx context.Context, rclient client.Client, sts *appsv1.Statef return err } } + for _, pvc := range pvcs.Items { + nsnPvc := types.NamespacedName{Name: pvc.Name, Namespace: pvc.Namespace} + size := pvc.Spec.Resources.Requests[corev1.ResourceStorage] + if err := waitForPVCReady(ctx, rclient, nsnPvc, size); err != nil { + return err + } + } return nil } diff --git a/internal/controller/operator/factory/reconcile/statefulset_pvc_expand_test.go b/internal/controller/operator/factory/reconcile/statefulset_pvc_expand_test.go index 6014a529c..1a36fea4d 100644 --- a/internal/controller/operator/factory/reconcile/statefulset_pvc_expand_test.go +++ b/internal/controller/operator/factory/reconcile/statefulset_pvc_expand_test.go @@ -160,10 +160,10 @@ func Test_updateSTSPVC(t *testing.T) { } f := func(o opts) { t.Helper() - cl := k8stools.GetTestClientWithActions(nil) + cl := k8stools.GetTestClientWithActionsAndObjects(nil) ctx := context.TODO() if o.preRun != nil { - o.preRun(cl.Client) + o.preRun(cl) cl.Actions = nil } err := updateSTSPVC(ctx, cl, o.sts, o.prevVCTs) @@ -226,6 +226,11 @@ func Test_updateSTSPVC(t *testing.T) { }, }, }, + Status: corev1.PersistentVolumeClaimStatus{ + Capacity: map[corev1.ResourceName]resource.Quantity{ + corev1.ResourceStorage: resource.MustParse("10Gi"), + }, + }, }, } }), @@ -294,7 +299,7 @@ func Test_updateSTSPVC(t *testing.T) { "test": "test", "3rd-party": "value", }, - ResourceVersion: "1", + ResourceVersion: "2", }, Spec: corev1.PersistentVolumeClaimSpec{ Resources: corev1.VolumeResourceRequirements{ @@ -303,6 +308,11 @@ func Test_updateSTSPVC(t *testing.T) { }, }, }, + Status: corev1.PersistentVolumeClaimStatus{ + Capacity: map[corev1.ResourceName]resource.Quantity{ + corev1.ResourceStorage: resource.MustParse("10Gi"), + }, + }, }, }, }) @@ -383,6 +393,7 @@ func Test_updateSTSPVC(t *testing.T) { }, actions: []k8stools.ClientAction{ {Verb: "Update", Kind: "PersistentVolumeClaim", Resource: pvc1NSN}, + {Verb: "Get", Kind: "PersistentVolumeClaim", Resource: pvc1NSN}, }, expected: []corev1.PersistentVolumeClaim{ { @@ -398,7 +409,7 @@ func Test_updateSTSPVC(t *testing.T) { "test": "after", "3rd-party": "value", }, - ResourceVersion: "2", + ResourceVersion: "4", }, Spec: corev1.PersistentVolumeClaimSpec{ Resources: corev1.VolumeResourceRequirements{ @@ -407,6 +418,11 @@ func Test_updateSTSPVC(t *testing.T) { }, }, }, + Status: corev1.PersistentVolumeClaimStatus{ + Capacity: map[corev1.ResourceName]resource.Quantity{ + corev1.ResourceStorage: resource.MustParse("10Gi"), + }, + }, }, }, }) @@ -491,6 +507,8 @@ func Test_updateSTSPVC(t *testing.T) { actions: []k8stools.ClientAction{ {Verb: "Update", Kind: "PersistentVolumeClaim", Resource: pvc2NSN}, {Verb: "Update", Kind: "PersistentVolumeClaim", Resource: pvc1NSN}, + {Verb: "Get", Kind: "PersistentVolumeClaim", Resource: pvc2NSN}, + {Verb: "Get", Kind: "PersistentVolumeClaim", Resource: pvc1NSN}, }, expected: []corev1.PersistentVolumeClaim{ { @@ -501,7 +519,7 @@ func Test_updateSTSPVC(t *testing.T) { Annotations: map[string]string{ "operator.victoriametrics.com/pvc-allow-volume-expansion": "true", }, - ResourceVersion: "2", + ResourceVersion: "4", }, Spec: corev1.PersistentVolumeClaimSpec{ Resources: corev1.VolumeResourceRequirements{ @@ -510,6 +528,11 @@ func Test_updateSTSPVC(t *testing.T) { }, }, }, + Status: corev1.PersistentVolumeClaimStatus{ + Capacity: map[corev1.ResourceName]resource.Quantity{ + corev1.ResourceStorage: resource.MustParse("5Gi"), + }, + }, }, { ObjectMeta: metav1.ObjectMeta{ @@ -519,7 +542,7 @@ func Test_updateSTSPVC(t *testing.T) { Annotations: map[string]string{ "operator.victoriametrics.com/pvc-allow-volume-expansion": "true", }, - ResourceVersion: "2", + ResourceVersion: "4", }, Spec: corev1.PersistentVolumeClaimSpec{ Resources: corev1.VolumeResourceRequirements{ @@ -528,6 +551,11 @@ func Test_updateSTSPVC(t *testing.T) { }, }, }, + Status: corev1.PersistentVolumeClaimStatus{ + Capacity: map[corev1.ResourceName]resource.Quantity{ + corev1.ResourceStorage: resource.MustParse("15Gi"), + }, + }, }, }, }) @@ -581,7 +609,7 @@ func Test_updateSTSPVC(t *testing.T) { Name: pvc1NSN.Name, Namespace: pvc1NSN.Namespace, Labels: map[string]string{"app": "vmselect"}, - ResourceVersion: "1", + ResourceVersion: "2", }, Spec: corev1.PersistentVolumeClaimSpec{ Resources: corev1.VolumeResourceRequirements{ @@ -590,6 +618,11 @@ func Test_updateSTSPVC(t *testing.T) { }, }, }, + Status: corev1.PersistentVolumeClaimStatus{ + Capacity: map[corev1.ResourceName]resource.Quantity{ + corev1.ResourceStorage: resource.MustParse("10Gi"), + }, + }, }, }, }) @@ -643,6 +676,7 @@ func Test_updateSTSPVC(t *testing.T) { }, actions: []k8stools.ClientAction{ {Verb: "Update", Kind: "PersistentVolumeClaim", Resource: pvc1NSN}, + {Verb: "Get", Kind: "PersistentVolumeClaim", Resource: pvc1NSN}, }, expected: []corev1.PersistentVolumeClaim{ { @@ -653,7 +687,7 @@ func Test_updateSTSPVC(t *testing.T) { Annotations: map[string]string{ "operator.victoriametrics.com/pvc-allow-volume-expansion": "true", }, - ResourceVersion: "2", + ResourceVersion: "4", }, Spec: corev1.PersistentVolumeClaimSpec{ Resources: corev1.VolumeResourceRequirements{ @@ -662,6 +696,11 @@ func Test_updateSTSPVC(t *testing.T) { }, }, }, + Status: corev1.PersistentVolumeClaimStatus{ + Capacity: map[corev1.ResourceName]resource.Quantity{ + corev1.ResourceStorage: resource.MustParse("15Gi"), + }, + }, }, }, }) @@ -725,6 +764,7 @@ func Test_updateSTSPVC(t *testing.T) { }, actions: []k8stools.ClientAction{ {Verb: "Update", Kind: "PersistentVolumeClaim", Resource: pvc1NSN}, + {Verb: "Get", Kind: "PersistentVolumeClaim", Resource: pvc1NSN}, }, expected: []corev1.PersistentVolumeClaim{ { @@ -735,7 +775,7 @@ func Test_updateSTSPVC(t *testing.T) { Annotations: map[string]string{ "operator.victoriametrics.com/pvc-allow-volume-expansion": "true", }, - ResourceVersion: "2", + ResourceVersion: "4", }, Spec: corev1.PersistentVolumeClaimSpec{ Resources: corev1.VolumeResourceRequirements{ @@ -744,6 +784,11 @@ func Test_updateSTSPVC(t *testing.T) { }, }, }, + Status: corev1.PersistentVolumeClaimStatus{ + Capacity: map[corev1.ResourceName]resource.Quantity{ + corev1.ResourceStorage: resource.MustParse("15Gi"), + }, + }, }, }, }) @@ -772,7 +817,7 @@ func Test_updateSTSPVC(t *testing.T) { Name: "orphan-vmselect-0", Namespace: "default", Labels: map[string]string{"app": "vmselect"}, - ResourceVersion: "1", + ResourceVersion: "2", }, }, }, @@ -825,7 +870,7 @@ func Test_updateSTSPVC(t *testing.T) { Name: "data-vmselect-0", Namespace: "default", Labels: map[string]string{"app": "vmselect"}, - ResourceVersion: "1", + ResourceVersion: "2", }, Spec: corev1.PersistentVolumeClaimSpec{ Resources: corev1.VolumeResourceRequirements{ @@ -834,6 +879,11 @@ func Test_updateSTSPVC(t *testing.T) { }, }, }, + Status: corev1.PersistentVolumeClaimStatus{ + Capacity: map[corev1.ResourceName]resource.Quantity{ + corev1.ResourceStorage: resource.MustParse("20Gi"), + }, + }, }, }, }) diff --git a/internal/controller/operator/factory/reconcile/vmagent.go b/internal/controller/operator/factory/reconcile/vmagent.go index 583279ab5..a85734128 100644 --- a/internal/controller/operator/factory/reconcile/vmagent.go +++ b/internal/controller/operator/factory/reconcile/vmagent.go @@ -59,7 +59,7 @@ func VMAgent(ctx context.Context, rclient client.Client, newObj, prevObj *vmv1be if err != nil { return err } - if err := waitForStatus(ctx, rclient, newObj, vmStatusInterval, vmv1beta1.UpdateStatusOperational); err != nil { + if err := waitForStatus(ctx, rclient, newObj, vmWaitReadyInterval, vmv1beta1.UpdateStatusOperational); err != nil { return fmt.Errorf("failed to wait for VMAgent=%s to be ready: %w", nsn.String(), err) } return nil diff --git a/internal/controller/operator/factory/reconcile/vmauth.go b/internal/controller/operator/factory/reconcile/vmauth.go index b7a1f90b8..97c09caea 100644 --- a/internal/controller/operator/factory/reconcile/vmauth.go +++ b/internal/controller/operator/factory/reconcile/vmauth.go @@ -58,7 +58,7 @@ func VMAuth(ctx context.Context, rclient client.Client, newObj, prevObj *vmv1bet if err != nil { return err } - if err := waitForStatus(ctx, rclient, newObj, vmStatusInterval, vmv1beta1.UpdateStatusOperational); err != nil { + if err := waitForStatus(ctx, rclient, newObj, vmWaitReadyInterval, vmv1beta1.UpdateStatusOperational); err != nil { return fmt.Errorf("failed to wait for VMAuth=%s to be ready: %w", nsn.String(), err) } return nil diff --git a/internal/controller/operator/factory/reconcile/vmcluster.go b/internal/controller/operator/factory/reconcile/vmcluster.go index bb114929f..0c2336779 100644 --- a/internal/controller/operator/factory/reconcile/vmcluster.go +++ b/internal/controller/operator/factory/reconcile/vmcluster.go @@ -58,7 +58,7 @@ func VMCluster(ctx context.Context, rclient client.Client, newObj, prevObj *vmv1 if err != nil { return err } - if err := waitForStatus(ctx, rclient, newObj, vmStatusInterval, vmv1beta1.UpdateStatusOperational); err != nil { + if err := waitForStatus(ctx, rclient, newObj, vmWaitReadyInterval, vmv1beta1.UpdateStatusOperational); err != nil { return fmt.Errorf("failed to wait for VMCluster=%s to be ready: %w", nsn.String(), err) } return nil diff --git a/internal/manager/manager.go b/internal/manager/manager.go index 823430a1e..68b1db9ed 100644 --- a/internal/manager/manager.go +++ b/internal/manager/manager.go @@ -232,7 +232,7 @@ func RunManager(ctx context.Context) error { } } - reconcile.Init(baseConfig.PodWaitReadyIntervalCheck, baseConfig.AppReadyTimeout, baseConfig.PodWaitReadyTimeout, 5*time.Second, *statusUpdateTTL) + reconcile.Init(baseConfig, *statusUpdateTTL) config := ctrl.GetConfigOrDie() config.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(float32(*clientQPS), *clientBurst) From a6294260159ab5c2de3799dd9f00324dbe8e3ab0 Mon Sep 17 00:00:00 2001 From: Vadim Rutkovsky Date: Wed, 18 Mar 2026 12:14:14 +0100 Subject: [PATCH 02/22] test: add tests for useProxyProtocol setting Ensure that we configure correct healthchecks for components with proxy protocol enabled --- test/e2e/vlagent_test.go | 19 +++++++++ test/e2e/vlcluster_test.go | 80 ++++++++++++++++++++++++++++++++++++++ test/e2e/vlsingle_test.go | 17 ++++++++ test/e2e/vmagent_test.go | 59 ++++++++++++++++++++++++++++ test/e2e/vmcluster_test.go | 74 +++++++++++++++++++++++++++++++++++ test/e2e/vmsingle_test.go | 11 ++++++ test/e2e/vtcluster_test.go | 51 ++++++++++++++++++++++++ test/e2e/vtsingle_test.go | 17 ++++++++ 8 files changed, 328 insertions(+) diff --git a/test/e2e/vlagent_test.go b/test/e2e/vlagent_test.go index 42f74e36e..57a2e716b 100644 --- a/test/e2e/vlagent_test.go +++ b/test/e2e/vlagent_test.go @@ -377,6 +377,25 @@ var _ = Describe("test vlagent Controller", Label("vl", "agent", "vlagent"), fun Expect(hasVolumeMount(vmc.VolumeMounts, "/var/run/secrets/kubernetes.io/serviceaccount")).To(HaveOccurred()) }, ), + Entry("with UseProxyProtocol", "proxy-protocol", + &vmv1.VLAgent{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: nsn.Name, + }, + Spec: vmv1.VLAgentSpec{ + CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + ReplicaCount: ptr.To[int32](1), + ExtraArgs: map[string]string{ + "httpListenAddr.useProxyProtocol": "true", + }, + }, + RemoteWrite: []vmv1.VLAgentRemoteWriteSpec{{URL: "http://localhost:9428/internal/insert"}}, + }, + }, + nil, + func(cr *vmv1.VLAgent) {}, + ), ) type testStep struct { setup func(*vmv1.VLAgent) diff --git a/test/e2e/vlcluster_test.go b/test/e2e/vlcluster_test.go index f3ac7c774..335d2b686 100644 --- a/test/e2e/vlcluster_test.go +++ b/test/e2e/vlcluster_test.go @@ -54,6 +54,86 @@ var _ = Describe("test vlcluster Controller", Label("vl", "cluster", "vlcluster" }, }, } + DescribeTable("should create vlcluster", + func(name string, cr *vmv1.VLCluster, verify func(cr *vmv1.VLCluster)) { + cr.Name = name + cr.Namespace = namespace + nsn.Name = name + Expect(k8sClient.Create(ctx, cr)).ToNot(HaveOccurred()) + Eventually(func() error { + return expectObjectStatusOperational(ctx, k8sClient, &vmv1.VLCluster{}, nsn) + }, eventualDeploymentAppReadyTimeout).ShouldNot(HaveOccurred()) + if verify != nil { + var created vmv1.VLCluster + Expect(k8sClient.Get(ctx, nsn, &created)).ToNot(HaveOccurred()) + verify(&created) + } + }, + Entry("with UseProxyProtocol on all components", "proxy-protocol", + &vmv1.VLCluster{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + }, + Spec: vmv1.VLClusterSpec{ + VLInsert: &vmv1.VLInsert{ + CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + ExtraArgs: map[string]string{ + "httpListenAddr.useProxyProtocol": "true", + }, + }, + }, + VLSelect: &vmv1.VLSelect{ + CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + ExtraArgs: map[string]string{ + "httpListenAddr.useProxyProtocol": "true", + }, + }, + }, + VLStorage: &vmv1.VLStorage{ + RetentionPeriod: "1", + CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + ReplicaCount: ptr.To[int32](1), + ExtraArgs: map[string]string{ + "httpListenAddr.useProxyProtocol": "true", + }, + }, + }, + }, + }, + nil, + ), + Entry("with RequestsLoadBalancer enabled", "with-lb", + &vmv1.VLCluster{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + }, + Spec: vmv1.VLClusterSpec{ + RequestsLoadBalancer: vmv1beta1.VMAuthLoadBalancer{ + Enabled: true, + }, + VLInsert: &vmv1.VLInsert{}, + VLSelect: &vmv1.VLSelect{}, + VLStorage: &vmv1.VLStorage{ + RetentionPeriod: "1", + CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + ReplicaCount: ptr.To[int32](1), + }, + }, + }, + }, + func(cr *vmv1.VLCluster) { + var dep appsv1.Deployment + nss := types.NamespacedName{Namespace: namespace, Name: cr.PrefixedName(vmv1beta1.ClusterComponentBalancer)} + Expect(k8sClient.Get(ctx, nss, &dep)).ToNot(HaveOccurred()) + var svc corev1.Service + Expect(k8sClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: cr.PrefixedName(vmv1beta1.ClusterComponentSelect)}, &svc)).ToNot(HaveOccurred()) + Expect(svc.Spec.Selector).To(Equal(cr.SelectorLabels(vmv1beta1.ClusterComponentBalancer))) + Expect(k8sClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: cr.PrefixedName(vmv1beta1.ClusterComponentInsert)}, &svc)).ToNot(HaveOccurred()) + Expect(svc.Spec.Selector).To(Equal(cr.SelectorLabels(vmv1beta1.ClusterComponentBalancer))) + }, + ), + ) + type testStep struct { setup func(*vmv1.VLCluster) modify func(*vmv1.VLCluster) diff --git a/test/e2e/vlsingle_test.go b/test/e2e/vlsingle_test.go index 6c4a0517c..5c4960f9a 100644 --- a/test/e2e/vlsingle_test.go +++ b/test/e2e/vlsingle_test.go @@ -159,6 +159,23 @@ var _ = Describe("test vlsingle Controller", Label("vl", "single", "vlsingle"), Expect(ts.Containers[0].VolumeMounts[1].Name).To(Equal("unused")) }), + Entry("with UseProxyProtocol", "proxy-protocol", + &vmv1.VLSingle{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + }, + Spec: vmv1.VLSingleSpec{ + CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + ReplicaCount: ptr.To[int32](1), + ExtraArgs: map[string]string{ + "httpListenAddr.useProxyProtocol": "true", + }, + }, + RetentionPeriod: "1", + }, + }, + func(cr *vmv1.VLSingle) {}, + ), ) baseVLSingle := &vmv1.VLSingle{ diff --git a/test/e2e/vmagent_test.go b/test/e2e/vmagent_test.go index 2b9a0d129..7b2ed92e6 100644 --- a/test/e2e/vmagent_test.go +++ b/test/e2e/vmagent_test.go @@ -386,6 +386,65 @@ var _ = Describe("test vmagent Controller", Label("vm", "agent", "vmagent"), fun Expect(vmc.VolumeMounts).To(HaveLen(5)) Expect(hasVolumeMount(vmc.VolumeMounts, "/var/run/secrets/kubernetes.io/serviceaccount")).ToNot(HaveOccurred()) }), + Entry("with UseProxyProtocol in deployment mode", "proxy-protocol-deploy", + &vmv1beta1.VMAgent{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: nsn.Name, + }, + Spec: vmv1beta1.VMAgentSpec{ + CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + ReplicaCount: ptr.To[int32](1), + ExtraArgs: map[string]string{ + "httpListenAddr.useProxyProtocol": "true", + }, + }, + RemoteWrite: []vmv1beta1.VMAgentRemoteWriteSpec{ + {URL: "http://localhost:8428"}, + }, + }, + }, nil, func(cr *vmv1beta1.VMAgent) {}, + ), + Entry("with UseProxyProtocol in statefulset mode", "proxy-protocol-sts", + &vmv1beta1.VMAgent{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: nsn.Name, + }, + Spec: vmv1beta1.VMAgentSpec{ + CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + ReplicaCount: ptr.To[int32](1), + ExtraArgs: map[string]string{ + "httpListenAddr.useProxyProtocol": "true", + }, + }, + StatefulMode: true, + RemoteWrite: []vmv1beta1.VMAgentRemoteWriteSpec{ + {URL: "http://localhost:8428"}, + }, + }, + }, nil, func(cr *vmv1beta1.VMAgent) {}, + ), + Entry("with UseProxyProtocol in daemonset mode", "proxy-protocol-ds", + &vmv1beta1.VMAgent{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: nsn.Name, + }, + Spec: vmv1beta1.VMAgentSpec{ + CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + ReplicaCount: ptr.To[int32](1), + ExtraArgs: map[string]string{ + "httpListenAddr.useProxyProtocol": "true", + }, + }, + DaemonSetMode: true, + RemoteWrite: []vmv1beta1.VMAgentRemoteWriteSpec{ + {URL: "http://localhost:8428"}, + }, + }, + }, nil, func(cr *vmv1beta1.VMAgent) {}, + ), ) type testStep struct { setup func(*vmv1beta1.VMAgent) diff --git a/test/e2e/vmcluster_test.go b/test/e2e/vmcluster_test.go index 4096b8379..dace800ad 100644 --- a/test/e2e/vmcluster_test.go +++ b/test/e2e/vmcluster_test.go @@ -351,6 +351,80 @@ var _ = Describe("e2e vmcluster", Label("vm", "cluster", "vmcluster"), func() { } }, ), + Entry("with UseProxyProtocol on all components", "proxy-protocol", + &vmv1beta1.VMCluster{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: nsn.Name, + }, + Spec: vmv1beta1.VMClusterSpec{ + RetentionPeriod: "1", + VMStorage: &vmv1beta1.VMStorage{ + CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + ReplicaCount: ptr.To[int32](1), + ExtraArgs: map[string]string{ + "httpListenAddr.useProxyProtocol": "true", + }, + }, + }, + VMSelect: &vmv1beta1.VMSelect{ + CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + ReplicaCount: ptr.To[int32](1), + ExtraArgs: map[string]string{ + "httpListenAddr.useProxyProtocol": "true", + }, + }, + }, + VMInsert: &vmv1beta1.VMInsert{ + CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + ReplicaCount: ptr.To[int32](1), + ExtraArgs: map[string]string{ + "httpListenAddr.useProxyProtocol": "true", + }, + }, + }, + }, + }, + nil, + ), + Entry("with RequestsLoadBalancer enabled", "with-lb", + &vmv1beta1.VMCluster{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: nsn.Name, + }, + Spec: vmv1beta1.VMClusterSpec{ + RetentionPeriod: "1", + RequestsLoadBalancer: vmv1beta1.VMAuthLoadBalancer{ + Enabled: true, + }, + VMStorage: &vmv1beta1.VMStorage{ + CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + ReplicaCount: ptr.To[int32](1), + }, + }, + VMSelect: &vmv1beta1.VMSelect{ + CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + ReplicaCount: ptr.To[int32](1), + }, + }, + VMInsert: &vmv1beta1.VMInsert{ + CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + ReplicaCount: ptr.To[int32](1), + }, + }, + }, + }, + func(cr *vmv1beta1.VMCluster) { + var lbDep appsv1.Deployment + nss := types.NamespacedName{Namespace: namespace, Name: cr.PrefixedName(vmv1beta1.ClusterComponentBalancer)} + Expect(k8sClient.Get(ctx, nss, &lbDep)).ToNot(HaveOccurred()) + var svc corev1.Service + Expect(k8sClient.Get(ctx, nss, &svc)).ToNot(HaveOccurred()) + Expect(k8sClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: cr.PrefixedInternalName(vmv1beta1.ClusterComponentInsert)}, &svc)).ToNot(HaveOccurred()) + Expect(k8sClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: cr.PrefixedInternalName(vmv1beta1.ClusterComponentSelect)}, &svc)).ToNot(HaveOccurred()) + }, + ), ) }) Context("update", func() { diff --git a/test/e2e/vmsingle_test.go b/test/e2e/vmsingle_test.go index e17cba4cf..1998e5743 100644 --- a/test/e2e/vmsingle_test.go +++ b/test/e2e/vmsingle_test.go @@ -503,6 +503,17 @@ var _ = Describe("test vmsingle Controller", Label("vm", "single"), func() { }, }, ), + Entry("by enabling UseProxyProtocol", "proxy-protocol", false, + baseSingle.DeepCopy(), + testStep{ + modify: func(cr *vmv1beta1.VMSingle) { + cr.Spec.ExtraArgs = map[string]string{ + "httpListenAddr.useProxyProtocol": "true", + } + }, + verify: func(cr *vmv1beta1.VMSingle) {}, + }, + ), ) }, ) diff --git a/test/e2e/vtcluster_test.go b/test/e2e/vtcluster_test.go index 8ca5dc72d..51e5f13a2 100644 --- a/test/e2e/vtcluster_test.go +++ b/test/e2e/vtcluster_test.go @@ -39,6 +39,57 @@ var _ = Describe("test vtcluster Controller", Label("vt", "cluster", "vtcluster" })).ToNot(HaveOccurred()) waitResourceDeleted(ctx, k8sClient, nsn, &vmv1.VTCluster{}) }) + + DescribeTable("should create", func(name string, cr *vmv1.VTCluster, verify func(cr *vmv1.VTCluster)) { + nsn.Name = name + cr.Name = name + Expect(k8sClient.Create(ctx, cr)).ToNot(HaveOccurred()) + Eventually(func() error { + return expectObjectStatusOperational(ctx, k8sClient, &vmv1.VTCluster{}, nsn) + }, eventualDeploymentAppReadyTimeout).ShouldNot(HaveOccurred()) + + var created vmv1.VTCluster + Expect(k8sClient.Get(ctx, nsn, &created)).ToNot(HaveOccurred()) + verify(&created) + }, + Entry("with UseProxyProtocol on all components", "proxy-protocol", + &vmv1.VTCluster{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: nsn.Name, + }, + Spec: vmv1.VTClusterSpec{ + Storage: &vmv1.VTStorage{ + RetentionPeriod: "1", + CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + ReplicaCount: ptr.To[int32](1), + ExtraArgs: map[string]string{ + "httpListenAddr.useProxyProtocol": "true", + }, + }, + }, + Select: &vmv1.VTSelect{ + CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + ReplicaCount: ptr.To[int32](1), + ExtraArgs: map[string]string{ + "httpListenAddr.useProxyProtocol": "true", + }, + }, + }, + Insert: &vmv1.VTInsert{ + CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + ReplicaCount: ptr.To[int32](1), + ExtraArgs: map[string]string{ + "httpListenAddr.useProxyProtocol": "true", + }, + }, + }, + }, + }, + func(cr *vmv1.VTCluster) {}, + ), + ) + baseVTCluster := &vmv1.VTCluster{ ObjectMeta: metav1.ObjectMeta{ Namespace: namespace, diff --git a/test/e2e/vtsingle_test.go b/test/e2e/vtsingle_test.go index a48b8cc23..394c3ce3d 100644 --- a/test/e2e/vtsingle_test.go +++ b/test/e2e/vtsingle_test.go @@ -159,6 +159,23 @@ var _ = Describe("test vtsingle Controller", Label("vt", "single", "vtsingle"), Expect(ts.Containers[0].VolumeMounts[1].Name).To(Equal("unused")) }), + Entry("with UseProxyProtocol", "proxy-protocol", + &vmv1.VTSingle{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + }, + Spec: vmv1.VTSingleSpec{ + CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + ReplicaCount: ptr.To[int32](1), + ExtraArgs: map[string]string{ + "httpListenAddr.useProxyProtocol": "true", + }, + }, + RetentionPeriod: "1", + }, + }, + func(cr *vmv1.VTSingle) {}, + ), ) baseVTSingle := &vmv1.VTSingle{ From de3c010b968ed5be619157107e58ecd5ca1c863c Mon Sep 17 00:00:00 2001 From: Andrii Chubatiuk Date: Fri, 6 Mar 2026 15:33:26 +0200 Subject: [PATCH 03/22] chore: refactored probe building and cluster defaults management (#1916) Co-authored-by: Vadim Rutkovsky --- api/operator/v1/vlagent_types.go | 17 +- api/operator/v1/vlcluster_types.go | 48 +- api/operator/v1/vlsingle_types.go | 14 +- api/operator/v1/vmanomaly_types.go | 18 +- api/operator/v1/vtcluster_types.go | 44 +- api/operator/v1/vtsingle_types.go | 16 +- api/operator/v1/zz_generated.deepcopy.go | 80 +-- api/operator/v1alpha1/vmdistributed_types.go | 6 +- .../v1alpha1/zz_generated.deepcopy.go | 8 +- api/operator/v1beta1/vlogs_types.go | 9 +- api/operator/v1beta1/vmagent_types.go | 21 +- api/operator/v1beta1/vmalert_types.go | 15 +- api/operator/v1beta1/vmalert_types_test.go | 2 +- api/operator/v1beta1/vmalertmanager_types.go | 15 +- api/operator/v1beta1/vmauth_types.go | 15 +- api/operator/v1beta1/vmcluster_types.go | 55 +- api/operator/v1beta1/vmextra_types.go | 114 ++-- api/operator/v1beta1/vmextra_types_test.go | 51 ++ api/operator/v1beta1/vmsingle_types.go | 19 +- api/operator/v1beta1/zz_generated.deepcopy.go | 192 ++---- config/crd/overlay/crd.yaml | 129 ++-- docs/api.md | 170 +++-- docs/env.md | 33 +- go.mod | 2 + go.sum | 4 +- internal/config/config.go | 139 ++-- .../operator/factory/build/build_test.go | 6 +- .../operator/factory/build/container.go | 122 ++-- .../operator/factory/build/container_test.go | 77 +-- .../operator/factory/build/daemonset.go | 5 +- .../operator/factory/build/defaults.go | 622 +++++------------- .../operator/factory/build/defaults_test.go | 204 +++++- .../operator/factory/build/deployment.go | 5 +- .../operator/factory/build/security.go | 32 +- .../operator/factory/build/security_test.go | 106 +-- .../operator/factory/build/service_account.go | 2 +- .../operator/factory/build/statefulset.go | 5 +- .../operator/factory/reconcile/service.go | 4 + .../factory/reconcile/vmagent_test.go | 2 +- .../operator/factory/reconcile/vmauth_test.go | 2 +- .../operator/factory/vlagent/vlagent.go | 16 +- .../operator/factory/vlagent/vlagent_test.go | 28 +- .../factory/vlcluster/vlcluster_test.go | 26 +- .../operator/factory/vlcluster/vlinsert.go | 6 +- .../operator/factory/vlcluster/vlselect.go | 6 +- .../operator/factory/vlcluster/vlstorage.go | 9 +- .../operator/factory/vlcluster/vmauth_lb.go | 6 +- .../operator/factory/vlsingle/vlsingle.go | 6 +- .../factory/vlsingle/vlsingle_test.go | 14 +- .../operator/factory/vmagent/vmagent.go | 59 +- .../factory/vmagent/vmagent_reconcile_test.go | 187 +++--- .../factory/vmagent/vmagent_scrapeconfig.go | 2 +- .../operator/factory/vmagent/vmagent_test.go | 35 +- .../operator/factory/vmalert/vmalert.go | 10 +- .../factory/vmalert/vmalert_reconcile_test.go | 171 ++--- .../operator/factory/vmalert/vmalert_test.go | 16 +- .../vmalertmanager/alertmanager_test.go | 10 +- .../factory/vmalertmanager/statefulset.go | 14 +- .../vmalertmanager_reconcile_test.go | 243 +++---- .../operator/factory/vmanomaly/pod.go | 10 +- .../operator/factory/vmanomaly/statefulset.go | 3 +- .../factory/vmanomaly/statefulset_test.go | 14 +- .../vmanomaly/vmanomaly_reconcile_test.go | 185 +++--- .../operator/factory/vmauth/vmauth.go | 36 +- .../factory/vmauth/vmauth_reconcile_test.go | 157 ++--- .../operator/factory/vmauth/vmauth_test.go | 14 +- .../factory/vmauth/vmusers_config_test.go | 4 +- .../operator/factory/vmcluster/vmcluster.go | 28 +- .../vmcluster/vmcluster_reconcile_test.go | 388 ++++------- .../factory/vmcluster/vmcluster_test.go | 44 +- .../factory/vmdistributed/util_test.go | 12 +- .../vmdistributed_reconcile_test.go | 141 ++-- .../vmdistributed/vmdistributed_test.go | 90 +-- .../operator/factory/vmsingle/vmsingle.go | 21 +- .../vmsingle/vmsingle_reconcile_test.go | 146 ++-- .../factory/vmsingle/vmsingle_test.go | 4 +- .../factory/vtcluster/cluster_test.go | 22 +- .../operator/factory/vtcluster/insert.go | 6 +- .../operator/factory/vtcluster/select.go | 6 +- .../operator/factory/vtcluster/storage.go | 9 +- .../operator/factory/vtcluster/vmauth_lb.go | 6 +- .../vtcluster/vtcluster_reconcile_test.go | 268 +++----- .../operator/factory/vtsingle/vtsingle.go | 7 +- .../vtsingle/vtsingle_reconcile_test.go | 141 ++-- .../factory/vtsingle/vtsingle_test.go | 14 +- test/e2e/childobjects/vmrule_test.go | 6 +- test/e2e/childobjects/vmuser_test.go | 4 +- test/e2e/suite/suite.go | 2 + test/e2e/vlagent_test.go | 30 +- test/e2e/vlcluster_test.go | 16 +- test/e2e/vlsingle_test.go | 20 +- test/e2e/vmagent_test.go | 44 +- test/e2e/vmalert_test.go | 16 +- test/e2e/vmalertmanager_test.go | 14 +- test/e2e/vmanomaly_test.go | 18 +- test/e2e/vmauth_test.go | 38 +- test/e2e/vmcluster_test.go | 241 ++++--- test/e2e/vmdistributed_test.go | 24 +- test/e2e/vmsingle_test.go | 34 +- test/e2e/vtcluster_test.go | 14 +- test/e2e/vtsingle_test.go | 20 +- 101 files changed, 2429 insertions(+), 3182 deletions(-) diff --git a/api/operator/v1/vlagent_types.go b/api/operator/v1/vlagent_types.go index b395adf09..c3e951f35 100644 --- a/api/operator/v1/vlagent_types.go +++ b/api/operator/v1/vlagent_types.go @@ -89,11 +89,8 @@ type VLAgentSpec struct { // ServiceAccountName is the name of the ServiceAccount to use to run the pods // +optional - ServiceAccountName string `json:"serviceAccountName,omitempty"` - - *vmv1beta1.EmbeddedProbes `json:",inline"` - vmv1beta1.CommonDefaultableParams `json:",inline,omitempty"` - vmv1beta1.CommonApplicationDeploymentParams `json:",inline,omitempty"` + ServiceAccountName string `json:"serviceAccountName,omitempty"` + vmv1beta1.CommonAppsParams `json:",inline,omitempty"` } type VLAgentK8sCollector struct { @@ -186,6 +183,11 @@ func (cr *VLAgent) Validate() error { return nil } +// UseProxyProtocol implements build.probeCRD interface +func (cr *VLAgent) UseProxyProtocol() bool { + return vmv1beta1.UseProxyProtocol(cr.Spec.ExtraArgs) +} + // UnmarshalJSON implements json.Unmarshaler interface func (cr *VLAgent) UnmarshalJSON(src []byte) error { type pcr VLAgent @@ -461,11 +463,6 @@ func (cr *VLAgent) AsURL() string { return fmt.Sprintf("%s://%s.%s.svc:%s", vmv1beta1.HTTPProtoFromFlags(cr.Spec.ExtraArgs), cr.PrefixedName(), cr.Namespace, port) } -// Probe implements build.probeCRD interface -func (cr *VLAgent) Probe() *vmv1beta1.EmbeddedProbes { - return cr.Spec.EmbeddedProbes -} - // ProbePath implements build.probeCRD interface func (cr *VLAgent) ProbePath() string { return vmv1beta1.BuildPathWithPrefixFlag(cr.Spec.ExtraArgs, healthPath) diff --git a/api/operator/v1/vlcluster_types.go b/api/operator/v1/vlcluster_types.go index f5de9433b..479487b6b 100644 --- a/api/operator/v1/vlcluster_types.go +++ b/api/operator/v1/vlcluster_types.go @@ -236,8 +236,7 @@ type VLInsert struct { ServiceScrapeSpec *vmv1beta1.VMServiceScrapeSpec `json:"serviceScrapeSpec,omitempty"` // PodDisruptionBudget created by operator // +optional - PodDisruptionBudget *vmv1beta1.EmbeddedPodDisruptionBudgetSpec `json:"podDisruptionBudget,omitempty"` - *vmv1beta1.EmbeddedProbes `json:",inline"` + PodDisruptionBudget *vmv1beta1.EmbeddedPodDisruptionBudgetSpec `json:"podDisruptionBudget,omitempty"` // Configures horizontal pod autoscaling. // +optional HPA *vmv1beta1.EmbeddedHPA `json:"hpa,omitempty"` @@ -256,13 +255,7 @@ type VLInsert struct { // +optional RollingUpdate *appsv1.RollingUpdateDeployment `json:"rollingUpdate,omitempty"` - vmv1beta1.CommonDefaultableParams `json:",inline"` - vmv1beta1.CommonApplicationDeploymentParams `json:",inline"` -} - -// Probe implements build.probeCRD interface -func (cr *VLInsert) Probe() *vmv1beta1.EmbeddedProbes { - return cr.EmbeddedProbes + vmv1beta1.CommonAppsParams `json:",inline"` } // ProbePath implements build.probeCRD interface @@ -270,6 +263,11 @@ func (cr *VLInsert) ProbePath() string { return vmv1beta1.BuildPathWithPrefixFlag(cr.ExtraArgs, healthPath) } +// UseProxyProtocol implements build.probeCRD interface +func (cr *VLInsert) UseProxyProtocol() bool { + return vmv1beta1.UseProxyProtocol(cr.ExtraArgs) +} + // ProbeScheme implements build.probeCRD interface func (cr *VLInsert) ProbeScheme() string { return strings.ToUpper(vmv1beta1.HTTPProtoFromFlags(cr.ExtraArgs)) @@ -421,8 +419,7 @@ type VLSelect struct { ServiceScrapeSpec *vmv1beta1.VMServiceScrapeSpec `json:"serviceScrapeSpec,omitempty"` // PodDisruptionBudget created by operator // +optional - PodDisruptionBudget *vmv1beta1.EmbeddedPodDisruptionBudgetSpec `json:"podDisruptionBudget,omitempty"` - *vmv1beta1.EmbeddedProbes `json:",inline"` + PodDisruptionBudget *vmv1beta1.EmbeddedPodDisruptionBudgetSpec `json:"podDisruptionBudget,omitempty"` // Configures horizontal pod autoscaling. // +optional HPA *vmv1beta1.EmbeddedHPA `json:"hpa,omitempty"` @@ -441,8 +438,7 @@ type VLSelect struct { // ExtraStorageNodes - defines additional storage nodes to VLSelect ExtraStorageNodes []VLStorageNode `json:"extraStorageNodes,omitempty"` - vmv1beta1.CommonDefaultableParams `json:",inline"` - vmv1beta1.CommonApplicationDeploymentParams `json:",inline"` + vmv1beta1.CommonAppsParams `json:",inline"` } // GetMetricsPath returns prefixed path for metric requests @@ -453,6 +449,11 @@ func (cr *VLSelect) GetMetricsPath() string { return vmv1beta1.BuildPathWithPrefixFlag(cr.ExtraArgs, metricsPath) } +// UseProxyProtocol implements build.probeCRD interface +func (cr *VLSelect) UseProxyProtocol() bool { + return vmv1beta1.UseProxyProtocol(cr.ExtraArgs) +} + // ExtraArgs returns additionally configured command-line arguments func (cr *VLSelect) GetExtraArgs() map[string]string { return cr.ExtraArgs @@ -468,11 +469,6 @@ func (cr *VLSelect) GetServiceScrape() *vmv1beta1.VMServiceScrapeSpec { return cr.ServiceScrapeSpec } -// Probe implements build.probeCRD interface -func (cr *VLSelect) Probe() *vmv1beta1.EmbeddedProbes { - return cr.EmbeddedProbes -} - // ProbePath implements build.probeCRD interface func (cr *VLSelect) ProbePath() string { return vmv1beta1.BuildPathWithPrefixFlag(cr.ExtraArgs, healthPath) @@ -537,8 +533,7 @@ type VLStorage struct { ServiceScrapeSpec *vmv1beta1.VMServiceScrapeSpec `json:"serviceScrapeSpec,omitempty"` // PodDisruptionBudget created by operator // +optional - PodDisruptionBudget *vmv1beta1.EmbeddedPodDisruptionBudgetSpec `json:"podDisruptionBudget,omitempty"` - *vmv1beta1.EmbeddedProbes `json:",inline"` + PodDisruptionBudget *vmv1beta1.EmbeddedPodDisruptionBudgetSpec `json:"podDisruptionBudget,omitempty"` // RollingUpdateStrategy defines strategy for application updates // Default is OnDelete, in this case operator handles update process // Can be changed for RollingUpdate @@ -574,8 +569,7 @@ type VLStorage struct { // +optional MaintenanceSelectNodeIDs []int32 `json:"maintenanceSelectNodeIDs,omitempty"` - vmv1beta1.CommonDefaultableParams `json:",inline"` - vmv1beta1.CommonApplicationDeploymentParams `json:",inline"` + vmv1beta1.CommonAppsParams `json:",inline"` // RollingUpdateStrategyBehavior defines customized behavior for rolling updates. // It applies if the RollingUpdateStrategy is set to OnDelete, which is the default. @@ -591,6 +585,11 @@ func (cr *VLStorage) GetStorageVolumeName() string { return "vlstorage-db" } +// UseProxyProtocol implements build.probeCRD interface +func (cr *VLStorage) UseProxyProtocol() bool { + return vmv1beta1.UseProxyProtocol(cr.ExtraArgs) +} + // GetMetricsPath returns prefixed path for metric requests func (cr *VLStorage) GetMetricsPath() string { if cr == nil { @@ -614,11 +613,6 @@ func (cr *VLStorage) GetServiceScrape() *vmv1beta1.VMServiceScrapeSpec { return cr.ServiceScrapeSpec } -// Probe implements build.probeCRD interface -func (cr *VLStorage) Probe() *vmv1beta1.EmbeddedProbes { - return cr.EmbeddedProbes -} - // ProbePath implements build.probeCRD interface func (cr *VLStorage) ProbePath() string { return vmv1beta1.BuildPathWithPrefixFlag(cr.ExtraArgs, healthPath) diff --git a/api/operator/v1/vlsingle_types.go b/api/operator/v1/vlsingle_types.go index 8119f999d..42c4445b6 100644 --- a/api/operator/v1/vlsingle_types.go +++ b/api/operator/v1/vlsingle_types.go @@ -43,8 +43,7 @@ type VLSingleSpec struct { // created by operator for the given CustomResource ManagedMetadata *vmv1beta1.ManagedObjectsMetadata `json:"managedMetadata,omitempty"` - vmv1beta1.CommonDefaultableParams `json:",inline,omitempty"` - vmv1beta1.CommonApplicationDeploymentParams `json:",inline,omitempty"` + vmv1beta1.CommonAppsParams `json:",inline,omitempty"` // LogLevel for VictoriaLogs to be configured with. // +optional @@ -96,8 +95,6 @@ type VLSingleSpec struct { // ServiceScrapeSpec that will be added to vlsingle VMServiceScrape spec // +optional ServiceScrapeSpec *vmv1beta1.VMServiceScrapeSpec `json:"serviceScrapeSpec,omitempty"` - // LivenessProbe that will be added to VLSingle pod - *vmv1beta1.EmbeddedProbes `json:",inline"` // ServiceAccountName is the name of the ServiceAccount to use to run the pods // +optional @@ -148,6 +145,11 @@ func (cr *VLSingle) GetStatus() *VLSingleStatus { return &cr.Status } +// UseProxyProtocol implements build.probeCRD interface +func (cr *VLSingle) UseProxyProtocol() bool { + return vmv1beta1.UseProxyProtocol(cr.Spec.ExtraArgs) +} + // DefaultStatusFields implements reconcile.ObjectWithDeepCopyAndStatus interface func (cr *VLSingle) DefaultStatusFields(vs *VLSingleStatus) { } @@ -211,10 +213,6 @@ func (cr *VLSingleSpec) UnmarshalJSON(src []byte) error { return nil } -func (cr *VLSingle) Probe() *vmv1beta1.EmbeddedProbes { - return cr.Spec.EmbeddedProbes -} - func (cr *VLSingle) ProbePath() string { return vmv1beta1.BuildPathWithPrefixFlag(cr.Spec.ExtraArgs, healthPath) } diff --git a/api/operator/v1/vmanomaly_types.go b/api/operator/v1/vmanomaly_types.go index b47c57ce7..6203c39f5 100644 --- a/api/operator/v1/vmanomaly_types.go +++ b/api/operator/v1/vmanomaly_types.go @@ -58,8 +58,7 @@ type VMAnomalySpec struct { ShardCount *int `json:"shardCount,omitempty"` // PodDisruptionBudget created by operator // +optional - PodDisruptionBudget *vmv1beta1.EmbeddedPodDisruptionBudgetSpec `json:"podDisruptionBudget,omitempty"` - *vmv1beta1.EmbeddedProbes `json:",inline"` + PodDisruptionBudget *vmv1beta1.EmbeddedPodDisruptionBudgetSpec `json:"podDisruptionBudget,omitempty"` // ConfigRawYaml - raw configuration for anomaly, // it helps it to start without secret. // priority -> hardcoded ConfigRaw -> ConfigRaw, provided by user -> ConfigSecret. @@ -105,9 +104,8 @@ type VMAnomalySpec struct { License *vmv1beta1.License `json:"license,omitempty"` // ServiceAccountName is the name of the ServiceAccount to use to run the pods // +optional - ServiceAccountName string `json:"serviceAccountName,omitempty"` - vmv1beta1.CommonDefaultableParams `json:",inline,omitempty"` - vmv1beta1.CommonApplicationDeploymentParams `json:",inline,omitempty"` + ServiceAccountName string `json:"serviceAccountName,omitempty"` + vmv1beta1.CommonAppsParams `json:",inline,omitempty"` } // VMAnomalyWritersSpec defines writer configuration for VMAnomaly @@ -403,11 +401,6 @@ func (cr *VMAnomaly) GetAdditionalService() *vmv1beta1.AdditionalServiceSpec { return nil } -// Probe implements build.probeCRD interface -func (cr *VMAnomaly) Probe() *vmv1beta1.EmbeddedProbes { - return cr.Spec.EmbeddedProbes -} - // ProbePath implements build.probeCRD interface func (cr *VMAnomaly) ProbePath() string { if cr.Spec.Server != nil && cr.Spec.Server.PathPrefix != "" { @@ -470,6 +463,11 @@ func (cr *VMAnomaly) Paused() bool { return cr.Spec.Paused } +// UseProxyProtocol implements build.probeCRD interface +func (cr *VMAnomaly) UseProxyProtocol() bool { + return false +} + // UnmarshalJSON implements json.Unmarshaler interface func (cr *VMAnomaly) UnmarshalJSON(src []byte) error { type pcr VMAnomaly diff --git a/api/operator/v1/vtcluster_types.go b/api/operator/v1/vtcluster_types.go index c1beeb135..4ecb655a9 100644 --- a/api/operator/v1/vtcluster_types.go +++ b/api/operator/v1/vtcluster_types.go @@ -231,8 +231,7 @@ type VTInsert struct { ServiceScrapeSpec *vmv1beta1.VMServiceScrapeSpec `json:"serviceScrapeSpec,omitempty"` // PodDisruptionBudget created by operator // +optional - PodDisruptionBudget *vmv1beta1.EmbeddedPodDisruptionBudgetSpec `json:"podDisruptionBudget,omitempty"` - *vmv1beta1.EmbeddedProbes `json:",inline"` + PodDisruptionBudget *vmv1beta1.EmbeddedPodDisruptionBudgetSpec `json:"podDisruptionBudget,omitempty"` // Configures horizontal pod autoscaling. // +optional HPA *vmv1beta1.EmbeddedHPA `json:"hpa,omitempty"` @@ -248,13 +247,12 @@ type VTInsert struct { // +optional RollingUpdate *appsv1.RollingUpdateDeployment `json:"rollingUpdate,omitempty"` - vmv1beta1.CommonDefaultableParams `json:",inline"` - vmv1beta1.CommonApplicationDeploymentParams `json:",inline"` + vmv1beta1.CommonAppsParams `json:",inline"` } -// Probe implements build.probeCRD interface -func (cr *VTInsert) Probe() *vmv1beta1.EmbeddedProbes { - return cr.EmbeddedProbes +// UseProxyProtocol implements build.probeCRD interface +func (cr *VTInsert) UseProxyProtocol() bool { + return vmv1beta1.UseProxyProtocol(cr.ExtraArgs) } // ProbePath implements build.probeCRD interface @@ -328,8 +326,7 @@ type VTSelect struct { ServiceScrapeSpec *vmv1beta1.VMServiceScrapeSpec `json:"serviceScrapeSpec,omitempty"` // PodDisruptionBudget created by operator // +optional - PodDisruptionBudget *vmv1beta1.EmbeddedPodDisruptionBudgetSpec `json:"podDisruptionBudget,omitempty"` - *vmv1beta1.EmbeddedProbes `json:",inline"` + PodDisruptionBudget *vmv1beta1.EmbeddedPodDisruptionBudgetSpec `json:"podDisruptionBudget,omitempty"` // Configures horizontal pod autoscaling. // +optional HPA *vmv1beta1.EmbeddedHPA `json:"hpa,omitempty"` @@ -348,8 +345,7 @@ type VTSelect struct { // ExtraStorageNodes - defines additional storage nodes to VTSelect ExtraStorageNodes []VTStorageNode `json:"extraStorageNodes,omitempty"` - vmv1beta1.CommonDefaultableParams `json:",inline"` - vmv1beta1.CommonApplicationDeploymentParams `json:",inline"` + vmv1beta1.CommonAppsParams `json:",inline"` } // GetMetricsPath returns prefixed path for metric requests @@ -360,6 +356,11 @@ func (cr *VTSelect) GetMetricsPath() string { return vmv1beta1.BuildPathWithPrefixFlag(cr.ExtraArgs, metricsPath) } +// UseProxyProtocol implements build.probeCRD interface +func (cr *VTSelect) UseProxyProtocol() bool { + return vmv1beta1.UseProxyProtocol(cr.ExtraArgs) +} + // ExtraArgs returns additionally configured command-line arguments func (cr *VTSelect) GetExtraArgs() map[string]string { return cr.ExtraArgs @@ -375,11 +376,6 @@ func (cr *VTSelect) GetServiceScrape() *vmv1beta1.VMServiceScrapeSpec { return cr.ServiceScrapeSpec } -// Probe implements build.probeCRD interface -func (cr *VTSelect) Probe() *vmv1beta1.EmbeddedProbes { - return cr.EmbeddedProbes -} - // ProbePath implements build.probeCRD interface func (cr *VTSelect) ProbePath() string { return vmv1beta1.BuildPathWithPrefixFlag(cr.ExtraArgs, healthPath) @@ -447,8 +443,7 @@ type VTStorage struct { ServiceScrapeSpec *vmv1beta1.VMServiceScrapeSpec `json:"serviceScrapeSpec,omitempty"` // PodDisruptionBudget created by operator // +optional - PodDisruptionBudget *vmv1beta1.EmbeddedPodDisruptionBudgetSpec `json:"podDisruptionBudget,omitempty"` - *vmv1beta1.EmbeddedProbes `json:",inline"` + PodDisruptionBudget *vmv1beta1.EmbeddedPodDisruptionBudgetSpec `json:"podDisruptionBudget,omitempty"` // RollingUpdateStrategy defines strategy for application updates // Default is OnDelete, in this case operator handles update process // Can be changed for RollingUpdate @@ -484,8 +479,7 @@ type VTStorage struct { // +optional MaintenanceSelectNodeIDs []int32 `json:"maintenanceSelectNodeIDs,omitempty"` - vmv1beta1.CommonDefaultableParams `json:",inline"` - vmv1beta1.CommonApplicationDeploymentParams `json:",inline"` + vmv1beta1.CommonAppsParams `json:",inline"` // RollingUpdateStrategyBehavior defines customized behavior for rolling updates. // It applies if the RollingUpdateStrategy is set to OnDelete, which is the default. @@ -501,6 +495,11 @@ func (cr *VTStorage) GetStorageVolumeName() string { return "vtstorage-db" } +// UseProxyProtocol implements build.probeCRD interface +func (cr *VTStorage) UseProxyProtocol() bool { + return vmv1beta1.UseProxyProtocol(cr.ExtraArgs) +} + // GetMetricsPath returns prefixed path for metric requests func (cr *VTStorage) GetMetricsPath() string { if cr == nil { @@ -524,11 +523,6 @@ func (cr *VTStorage) GetServiceScrape() *vmv1beta1.VMServiceScrapeSpec { return cr.ServiceScrapeSpec } -// Probe implements build.probeCRD interface -func (cr *VTStorage) Probe() *vmv1beta1.EmbeddedProbes { - return cr.EmbeddedProbes -} - // ProbePath implements build.probeCRD interface func (cr *VTStorage) ProbePath() string { return vmv1beta1.BuildPathWithPrefixFlag(cr.ExtraArgs, healthPath) diff --git a/api/operator/v1/vtsingle_types.go b/api/operator/v1/vtsingle_types.go index 8705ce889..5c0ec6958 100644 --- a/api/operator/v1/vtsingle_types.go +++ b/api/operator/v1/vtsingle_types.go @@ -43,8 +43,7 @@ type VTSingleSpec struct { // created by operator for the given CustomResource ManagedMetadata *vmv1beta1.ManagedObjectsMetadata `json:"managedMetadata,omitempty"` - vmv1beta1.CommonDefaultableParams `json:",inline,omitempty"` - vmv1beta1.CommonApplicationDeploymentParams `json:",inline,omitempty"` + vmv1beta1.CommonAppsParams `json:",inline,omitempty"` // LogLevel for VictoriaTraces to be configured with. // +optional @@ -95,9 +94,6 @@ type VTSingleSpec struct { // ServiceScrapeSpec that will be added to vtsingle VMServiceScrape spec // +optional ServiceScrapeSpec *vmv1beta1.VMServiceScrapeSpec `json:"serviceScrapeSpec,omitempty"` - // LivenessProbe that will be added to VTSingle pod - *vmv1beta1.EmbeddedProbes `json:",inline"` - // ServiceAccountName is the name of the ServiceAccount to use to run the pods // +optional ServiceAccountName string `json:"serviceAccountName,omitempty"` @@ -206,11 +202,6 @@ func (cr *VTSingleSpec) UnmarshalJSON(src []byte) error { return nil } -// Probe implements build.probeCRD interface -func (cr *VTSingle) Probe() *vmv1beta1.EmbeddedProbes { - return cr.Spec.EmbeddedProbes -} - // ProbePath implements build.probeCRD interface func (cr *VTSingle) ProbePath() string { return vmv1beta1.BuildPathWithPrefixFlag(cr.Spec.ExtraArgs, healthPath) @@ -344,6 +335,11 @@ func (cr *VTSingle) HasSpecChanges() (bool, error) { return vmv1beta1.HasStateChanges(cr.ObjectMeta, cr.Spec) } +// UseProxyProtocol implements build.probeCRD interface +func (cr *VTSingle) UseProxyProtocol() bool { + return vmv1beta1.UseProxyProtocol(cr.Spec.ExtraArgs) +} + func (cr *VTSingle) Paused() bool { return cr.Spec.Paused } diff --git a/api/operator/v1/zz_generated.deepcopy.go b/api/operator/v1/zz_generated.deepcopy.go index 5df1962d9..86e6fee5f 100644 --- a/api/operator/v1/zz_generated.deepcopy.go +++ b/api/operator/v1/zz_generated.deepcopy.go @@ -488,13 +488,7 @@ func (in *VLAgentSpec) DeepCopyInto(out *VLAgentSpec) { *out = new(v1beta1.License) (*in).DeepCopyInto(*out) } - if in.EmbeddedProbes != nil { - in, out := &in.EmbeddedProbes, &out.EmbeddedProbes - *out = new(v1beta1.EmbeddedProbes) - (*in).DeepCopyInto(*out) - } - in.CommonDefaultableParams.DeepCopyInto(&out.CommonDefaultableParams) - in.CommonApplicationDeploymentParams.DeepCopyInto(&out.CommonApplicationDeploymentParams) + in.CommonAppsParams.DeepCopyInto(&out.CommonAppsParams) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VLAgentSpec. @@ -677,11 +671,6 @@ func (in *VLInsert) DeepCopyInto(out *VLInsert) { *out = new(v1beta1.EmbeddedPodDisruptionBudgetSpec) (*in).DeepCopyInto(*out) } - if in.EmbeddedProbes != nil { - in, out := &in.EmbeddedProbes, &out.EmbeddedProbes - *out = new(v1beta1.EmbeddedProbes) - (*in).DeepCopyInto(*out) - } if in.HPA != nil { in, out := &in.HPA, &out.HPA *out = new(v1beta1.EmbeddedHPA) @@ -707,8 +696,7 @@ func (in *VLInsert) DeepCopyInto(out *VLInsert) { *out = new(appsv1.RollingUpdateDeployment) (*in).DeepCopyInto(*out) } - in.CommonDefaultableParams.DeepCopyInto(&out.CommonDefaultableParams) - in.CommonApplicationDeploymentParams.DeepCopyInto(&out.CommonApplicationDeploymentParams) + in.CommonAppsParams.DeepCopyInto(&out.CommonAppsParams) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VLInsert. @@ -744,11 +732,6 @@ func (in *VLSelect) DeepCopyInto(out *VLSelect) { *out = new(v1beta1.EmbeddedPodDisruptionBudgetSpec) (*in).DeepCopyInto(*out) } - if in.EmbeddedProbes != nil { - in, out := &in.EmbeddedProbes, &out.EmbeddedProbes - *out = new(v1beta1.EmbeddedProbes) - (*in).DeepCopyInto(*out) - } if in.HPA != nil { in, out := &in.HPA, &out.HPA *out = new(v1beta1.EmbeddedHPA) @@ -774,8 +757,7 @@ func (in *VLSelect) DeepCopyInto(out *VLSelect) { *out = make([]VLStorageNode, len(*in)) copy(*out, *in) } - in.CommonDefaultableParams.DeepCopyInto(&out.CommonDefaultableParams) - in.CommonApplicationDeploymentParams.DeepCopyInto(&out.CommonApplicationDeploymentParams) + in.CommonAppsParams.DeepCopyInto(&out.CommonAppsParams) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VLSelect. @@ -865,8 +847,7 @@ func (in *VLSingleSpec) DeepCopyInto(out *VLSingleSpec) { *out = new(v1beta1.ManagedObjectsMetadata) (*in).DeepCopyInto(*out) } - in.CommonDefaultableParams.DeepCopyInto(&out.CommonDefaultableParams) - in.CommonApplicationDeploymentParams.DeepCopyInto(&out.CommonApplicationDeploymentParams) + in.CommonAppsParams.DeepCopyInto(&out.CommonAppsParams) if in.Storage != nil { in, out := &in.Storage, &out.Storage *out = new(corev1.PersistentVolumeClaimSpec) @@ -888,11 +869,6 @@ func (in *VLSingleSpec) DeepCopyInto(out *VLSingleSpec) { *out = new(v1beta1.VMServiceScrapeSpec) (*in).DeepCopyInto(*out) } - if in.EmbeddedProbes != nil { - in, out := &in.EmbeddedProbes, &out.EmbeddedProbes - *out = new(v1beta1.EmbeddedProbes) - (*in).DeepCopyInto(*out) - } if in.SyslogSpec != nil { in, out := &in.SyslogSpec, &out.SyslogSpec *out = new(SyslogServerSpec) @@ -949,11 +925,6 @@ func (in *VLStorage) DeepCopyInto(out *VLStorage) { *out = new(v1beta1.EmbeddedPodDisruptionBudgetSpec) (*in).DeepCopyInto(*out) } - if in.EmbeddedProbes != nil { - in, out := &in.EmbeddedProbes, &out.EmbeddedProbes - *out = new(v1beta1.EmbeddedProbes) - (*in).DeepCopyInto(*out) - } if in.ClaimTemplates != nil { in, out := &in.ClaimTemplates, &out.ClaimTemplates *out = make([]corev1.PersistentVolumeClaim, len(*in)) @@ -991,8 +962,7 @@ func (in *VLStorage) DeepCopyInto(out *VLStorage) { *out = make([]int32, len(*in)) copy(*out, *in) } - in.CommonDefaultableParams.DeepCopyInto(&out.CommonDefaultableParams) - in.CommonApplicationDeploymentParams.DeepCopyInto(&out.CommonApplicationDeploymentParams) + in.CommonAppsParams.DeepCopyInto(&out.CommonAppsParams) if in.RollingUpdateStrategyBehavior != nil { in, out := &in.RollingUpdateStrategyBehavior, &out.RollingUpdateStrategyBehavior *out = new(v1beta1.StatefulSetUpdateStrategyBehavior) @@ -1251,11 +1221,6 @@ func (in *VMAnomalySpec) DeepCopyInto(out *VMAnomalySpec) { *out = new(v1beta1.EmbeddedPodDisruptionBudgetSpec) (*in).DeepCopyInto(*out) } - if in.EmbeddedProbes != nil { - in, out := &in.EmbeddedProbes, &out.EmbeddedProbes - *out = new(v1beta1.EmbeddedProbes) - (*in).DeepCopyInto(*out) - } if in.ConfigSecret != nil { in, out := &in.ConfigSecret, &out.ConfigSecret *out = new(corev1.SecretKeySelector) @@ -1303,8 +1268,7 @@ func (in *VMAnomalySpec) DeepCopyInto(out *VMAnomalySpec) { *out = new(v1beta1.License) (*in).DeepCopyInto(*out) } - in.CommonDefaultableParams.DeepCopyInto(&out.CommonDefaultableParams) - in.CommonApplicationDeploymentParams.DeepCopyInto(&out.CommonApplicationDeploymentParams) + in.CommonAppsParams.DeepCopyInto(&out.CommonAppsParams) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VMAnomalySpec. @@ -1521,11 +1485,6 @@ func (in *VTInsert) DeepCopyInto(out *VTInsert) { *out = new(v1beta1.EmbeddedPodDisruptionBudgetSpec) (*in).DeepCopyInto(*out) } - if in.EmbeddedProbes != nil { - in, out := &in.EmbeddedProbes, &out.EmbeddedProbes - *out = new(v1beta1.EmbeddedProbes) - (*in).DeepCopyInto(*out) - } if in.HPA != nil { in, out := &in.HPA, &out.HPA *out = new(v1beta1.EmbeddedHPA) @@ -1546,8 +1505,7 @@ func (in *VTInsert) DeepCopyInto(out *VTInsert) { *out = new(appsv1.RollingUpdateDeployment) (*in).DeepCopyInto(*out) } - in.CommonDefaultableParams.DeepCopyInto(&out.CommonDefaultableParams) - in.CommonApplicationDeploymentParams.DeepCopyInto(&out.CommonApplicationDeploymentParams) + in.CommonAppsParams.DeepCopyInto(&out.CommonAppsParams) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VTInsert. @@ -1583,11 +1541,6 @@ func (in *VTSelect) DeepCopyInto(out *VTSelect) { *out = new(v1beta1.EmbeddedPodDisruptionBudgetSpec) (*in).DeepCopyInto(*out) } - if in.EmbeddedProbes != nil { - in, out := &in.EmbeddedProbes, &out.EmbeddedProbes - *out = new(v1beta1.EmbeddedProbes) - (*in).DeepCopyInto(*out) - } if in.HPA != nil { in, out := &in.HPA, &out.HPA *out = new(v1beta1.EmbeddedHPA) @@ -1613,8 +1566,7 @@ func (in *VTSelect) DeepCopyInto(out *VTSelect) { *out = make([]VTStorageNode, len(*in)) copy(*out, *in) } - in.CommonDefaultableParams.DeepCopyInto(&out.CommonDefaultableParams) - in.CommonApplicationDeploymentParams.DeepCopyInto(&out.CommonApplicationDeploymentParams) + in.CommonAppsParams.DeepCopyInto(&out.CommonAppsParams) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VTSelect. @@ -1704,8 +1656,7 @@ func (in *VTSingleSpec) DeepCopyInto(out *VTSingleSpec) { *out = new(v1beta1.ManagedObjectsMetadata) (*in).DeepCopyInto(*out) } - in.CommonDefaultableParams.DeepCopyInto(&out.CommonDefaultableParams) - in.CommonApplicationDeploymentParams.DeepCopyInto(&out.CommonApplicationDeploymentParams) + in.CommonAppsParams.DeepCopyInto(&out.CommonAppsParams) if in.Storage != nil { in, out := &in.Storage, &out.Storage *out = new(corev1.PersistentVolumeClaimSpec) @@ -1722,11 +1673,6 @@ func (in *VTSingleSpec) DeepCopyInto(out *VTSingleSpec) { *out = new(v1beta1.VMServiceScrapeSpec) (*in).DeepCopyInto(*out) } - if in.EmbeddedProbes != nil { - in, out := &in.EmbeddedProbes, &out.EmbeddedProbes - *out = new(v1beta1.EmbeddedProbes) - (*in).DeepCopyInto(*out) - } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VTSingleSpec. @@ -1778,11 +1724,6 @@ func (in *VTStorage) DeepCopyInto(out *VTStorage) { *out = new(v1beta1.EmbeddedPodDisruptionBudgetSpec) (*in).DeepCopyInto(*out) } - if in.EmbeddedProbes != nil { - in, out := &in.EmbeddedProbes, &out.EmbeddedProbes - *out = new(v1beta1.EmbeddedProbes) - (*in).DeepCopyInto(*out) - } if in.ClaimTemplates != nil { in, out := &in.ClaimTemplates, &out.ClaimTemplates *out = make([]corev1.PersistentVolumeClaim, len(*in)) @@ -1820,8 +1761,7 @@ func (in *VTStorage) DeepCopyInto(out *VTStorage) { *out = make([]int32, len(*in)) copy(*out, *in) } - in.CommonDefaultableParams.DeepCopyInto(&out.CommonDefaultableParams) - in.CommonApplicationDeploymentParams.DeepCopyInto(&out.CommonApplicationDeploymentParams) + in.CommonAppsParams.DeepCopyInto(&out.CommonAppsParams) if in.RollingUpdateStrategyBehavior != nil { in, out := &in.RollingUpdateStrategyBehavior, &out.RollingUpdateStrategyBehavior *out = new(v1beta1.StatefulSetUpdateStrategyBehavior) diff --git a/api/operator/v1alpha1/vmdistributed_types.go b/api/operator/v1alpha1/vmdistributed_types.go index a997ff533..362d6386b 100644 --- a/api/operator/v1alpha1/vmdistributed_types.go +++ b/api/operator/v1alpha1/vmdistributed_types.go @@ -185,8 +185,7 @@ type VMDistributedZoneAgentSpec struct { RollingUpdate *appsv1.RollingUpdateDeployment `json:"rollingUpdate,omitempty"` // PodDisruptionBudget created by operator // +optional - PodDisruptionBudget *vmv1beta1.EmbeddedPodDisruptionBudgetSpec `json:"podDisruptionBudget,omitempty"` - *vmv1beta1.EmbeddedProbes `json:",inline"` + PodDisruptionBudget *vmv1beta1.EmbeddedPodDisruptionBudgetSpec `json:"podDisruptionBudget,omitempty"` // StatefulMode enables StatefulSet for `VMAgent` instead of Deployment // it allows using persistent storage for vmagent's persistentQueue // +optional @@ -214,8 +213,7 @@ type VMDistributedZoneAgentSpec struct { // +optional ServiceAccountName string `json:"serviceAccountName,omitempty"` - vmv1beta1.CommonDefaultableParams `json:",inline,omitempty"` - vmv1beta1.CommonApplicationDeploymentParams `json:",inline,omitempty"` + vmv1beta1.CommonAppsParams `json:",inline,omitempty"` } func (s *VMDistributedZoneAgentSpec) ToVMAgentSpec() (*vmv1beta1.VMAgentSpec, error) { diff --git a/api/operator/v1alpha1/zz_generated.deepcopy.go b/api/operator/v1alpha1/zz_generated.deepcopy.go index b75d802c5..0ca17553b 100644 --- a/api/operator/v1alpha1/zz_generated.deepcopy.go +++ b/api/operator/v1alpha1/zz_generated.deepcopy.go @@ -220,11 +220,6 @@ func (in *VMDistributedZoneAgentSpec) DeepCopyInto(out *VMDistributedZoneAgentSp *out = new(v1beta1.EmbeddedPodDisruptionBudgetSpec) (*in).DeepCopyInto(*out) } - if in.EmbeddedProbes != nil { - in, out := &in.EmbeddedProbes, &out.EmbeddedProbes - *out = new(v1beta1.EmbeddedProbes) - (*in).DeepCopyInto(*out) - } if in.StatefulStorage != nil { in, out := &in.StatefulStorage, &out.StatefulStorage *out = new(v1beta1.StorageSpec) @@ -247,8 +242,7 @@ func (in *VMDistributedZoneAgentSpec) DeepCopyInto(out *VMDistributedZoneAgentSp *out = new(v1beta1.License) (*in).DeepCopyInto(*out) } - in.CommonDefaultableParams.DeepCopyInto(&out.CommonDefaultableParams) - in.CommonApplicationDeploymentParams.DeepCopyInto(&out.CommonApplicationDeploymentParams) + in.CommonAppsParams.DeepCopyInto(&out.CommonAppsParams) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VMDistributedZoneAgentSpec. diff --git a/api/operator/v1beta1/vlogs_types.go b/api/operator/v1beta1/vlogs_types.go index 06b796fe4..53cdc4789 100644 --- a/api/operator/v1beta1/vlogs_types.go +++ b/api/operator/v1beta1/vlogs_types.go @@ -44,8 +44,7 @@ type VLogsSpec struct { // created by operator for the given CustomResource ManagedMetadata *ManagedObjectsMetadata `json:"managedMetadata,omitempty"` - CommonDefaultableParams `json:",inline,omitempty"` - CommonApplicationDeploymentParams `json:",inline,omitempty"` + CommonAppsParams `json:",inline,omitempty"` // LogLevel for VictoriaLogs to be configured with. // +optional @@ -86,8 +85,6 @@ type VLogsSpec struct { // ServiceScrapeSpec that will be added to vlogs VMServiceScrape spec // +optional ServiceScrapeSpec *VMServiceScrapeSpec `json:"serviceScrapeSpec,omitempty"` - // LivenessProbe that will be added to VLogs pod - *EmbeddedProbes `json:",inline"` // ServiceAccountName is the name of the ServiceAccount to use to run the pods // +optional @@ -197,10 +194,6 @@ func (cr *VLogsSpec) UnmarshalJSON(src []byte) error { return nil } -func (cr *VLogs) Probe() *EmbeddedProbes { - return cr.Spec.EmbeddedProbes -} - func (cr *VLogs) ProbePath() string { return BuildPathWithPrefixFlag(cr.Spec.ExtraArgs, healthPath) } diff --git a/api/operator/v1beta1/vmagent_types.go b/api/operator/v1beta1/vmagent_types.go index a4f26630a..9fd525b38 100644 --- a/api/operator/v1beta1/vmagent_types.go +++ b/api/operator/v1beta1/vmagent_types.go @@ -79,7 +79,6 @@ type VMAgentSpec struct { // PodDisruptionBudget created by operator // +optional PodDisruptionBudget *EmbeddedPodDisruptionBudgetSpec `json:"podDisruptionBudget,omitempty"` - *EmbeddedProbes `json:",inline"` // DaemonSetMode enables DaemonSet deployment mode instead of Deployment. // Supports only VMPodScrape // (available from v0.55.0). @@ -114,11 +113,10 @@ type VMAgentSpec struct { // +optional ServiceAccountName string `json:"serviceAccountName,omitempty"` - CommonRelabelParams `json:",inline,omitempty"` - CommonScrapeParams `json:",inline,omitempty"` - CommonDefaultableParams `json:",inline,omitempty"` - CommonConfigReloaderParams `json:",inline,omitempty"` - CommonApplicationDeploymentParams `json:",inline,omitempty"` + CommonRelabelParams `json:",inline,omitempty"` + CommonScrapeParams `json:",inline,omitempty"` + CommonConfigReloaderParams `json:",inline,omitempty"` + CommonAppsParams `json:",inline,omitempty"` } // SetLastSpec implements objectWithLastAppliedState interface @@ -226,12 +224,9 @@ func (cr *VMAgent) GetReloaderParams() *CommonConfigReloaderParams { return &cr.Spec.CommonConfigReloaderParams } -// UseProxyProtocol implements reloadable interface +// UseProxyProtocol implements build.probeCRD interface func (cr *VMAgent) UseProxyProtocol() bool { - if v, ok := cr.Spec.ExtraArgs["httpListenAddr.useProxyProtocol"]; ok && v == "true" { - return true - } - return false + return UseProxyProtocol(cr.Spec.ExtraArgs) } // AutomountServiceAccountToken implements reloadable interface @@ -590,10 +585,6 @@ func (cr *VMAgent) AsURL() string { return fmt.Sprintf("%s://%s.%s.svc:%s", HTTPProtoFromFlags(cr.Spec.ExtraArgs), cr.PrefixedName(), cr.Namespace, port) } -func (cr *VMAgent) Probe() *EmbeddedProbes { - return cr.Spec.EmbeddedProbes -} - func (cr *VMAgent) ProbePath() string { return BuildPathWithPrefixFlag(cr.Spec.ExtraArgs, healthPath) } diff --git a/api/operator/v1beta1/vmalert_types.go b/api/operator/v1beta1/vmalert_types.go index b08c42fa3..0e63b72bb 100644 --- a/api/operator/v1beta1/vmalert_types.go +++ b/api/operator/v1beta1/vmalert_types.go @@ -137,7 +137,6 @@ type VMAlertSpec struct { // PodDisruptionBudget created by operator // +optional PodDisruptionBudget *EmbeddedPodDisruptionBudgetSpec `json:"podDisruptionBudget,omitempty"` - *EmbeddedProbes `json:",inline"` // License allows to configure license key to be used for enterprise features. // Using license key is supported starting from VictoriaMetrics v1.94.0. // See [here](https://docs.victoriametrics.com/victoriametrics/enterprise/) @@ -148,9 +147,8 @@ type VMAlertSpec struct { // +optional ServiceAccountName string `json:"serviceAccountName,omitempty"` - CommonDefaultableParams `json:",inline,omitempty"` - CommonConfigReloaderParams `json:",inline,omitempty"` - CommonApplicationDeploymentParams `json:",inline,omitempty"` + CommonConfigReloaderParams `json:",inline,omitempty"` + CommonAppsParams `json:",inline,omitempty"` } // SetLastSpec implements objectWithLastAppliedState interface @@ -168,10 +166,9 @@ func (cr *VMAlert) GetReloaderParams() *CommonConfigReloaderParams { return &cr.Spec.CommonConfigReloaderParams } -// UseProxyProtocol implements reloadable interface +// UseProxyProtocol implements build.probeCRD interface func (cr *VMAlert) UseProxyProtocol() bool { - v, ok := cr.Spec.ExtraArgs["httpListenAddr.useProxyProtocol"] - return ok && v == "true" + return UseProxyProtocol(cr.Spec.ExtraArgs) } // AutomountServiceAccountToken implements reloadable interface @@ -320,10 +317,6 @@ func (cr *VMAlert) GetStatus() *VMAlertStatus { func (cr *VMAlert) DefaultStatusFields(vs *VMAlertStatus) { } -func (cr *VMAlert) Probe() *EmbeddedProbes { - return cr.Spec.EmbeddedProbes -} - func (cr *VMAlert) ProbePath() string { return BuildPathWithPrefixFlag(cr.Spec.ExtraArgs, healthPath) } diff --git a/api/operator/v1beta1/vmalert_types_test.go b/api/operator/v1beta1/vmalert_types_test.go index 78d54fca0..1fb87b122 100644 --- a/api/operator/v1beta1/vmalert_types_test.go +++ b/api/operator/v1beta1/vmalert_types_test.go @@ -22,7 +22,7 @@ func TestVMAlert_ValidateOk(t *testing.T) { } f(VMAlertSpec{ Datasource: VMAlertDatasourceSpec{URL: "http://some-url"}, - CommonApplicationDeploymentParams: CommonApplicationDeploymentParams{ + CommonAppsParams: CommonAppsParams{ ExtraArgs: map[string]string{"notifier.blackhole": "true"}, }, }) diff --git a/api/operator/v1beta1/vmalertmanager_types.go b/api/operator/v1beta1/vmalertmanager_types.go index a35def818..69d69dfe8 100644 --- a/api/operator/v1beta1/vmalertmanager_types.go +++ b/api/operator/v1beta1/vmalertmanager_types.go @@ -143,7 +143,6 @@ type VMAlertmanagerSpec struct { // PodDisruptionBudget created by operator // +optional PodDisruptionBudget *EmbeddedPodDisruptionBudgetSpec `json:"podDisruptionBudget,omitempty"` - *EmbeddedProbes `json:",inline"` // SelectAllByDefault changes default behavior for empty CRD selectors, such ConfigSelector. // with selectAllByDefault: true and undefined ConfigSelector and ConfigNamespaceSelector // Operator selects all exist alertManagerConfigs @@ -210,9 +209,8 @@ type VMAlertmanagerSpec struct { // +optional ServiceAccountName string `json:"serviceAccountName,omitempty"` - CommonDefaultableParams `json:",inline,omitempty"` - CommonConfigReloaderParams `json:",inline,omitempty"` - CommonApplicationDeploymentParams `json:",inline,omitempty"` + CommonConfigReloaderParams `json:",inline,omitempty"` + CommonAppsParams `json:",inline,omitempty"` } // SetLastSpec implements objectWithLastAppliedState interface @@ -235,11 +233,8 @@ func (cr *VMAlertmanager) GetReloaderParams() *CommonConfigReloaderParams { return &cr.Spec.CommonConfigReloaderParams } -// UseProxyProtocol implements reloadable interface +// UseProxyProtocol implements build.probeCRD interface func (cr *VMAlertmanager) UseProxyProtocol() bool { - if v, ok := cr.Spec.ExtraArgs["httpListenAddr.useProxyProtocol"]; ok && v == "true" { - return true - } return false } @@ -461,10 +456,6 @@ func (cr *VMAlertmanager) GetVolumeName() string { return fmt.Sprintf("vmalertmanager-%s-db", cr.Name) } -func (cr *VMAlertmanager) Probe() *EmbeddedProbes { - return cr.Spec.EmbeddedProbes -} - func (cr *VMAlertmanager) ProbePath() string { webRoutePrefix := "/" if cr.Spec.RoutePrefix != "" { diff --git a/api/operator/v1beta1/vmauth_types.go b/api/operator/v1beta1/vmauth_types.go index 79c09dc3e..63976ca49 100644 --- a/api/operator/v1beta1/vmauth_types.go +++ b/api/operator/v1beta1/vmauth_types.go @@ -70,8 +70,6 @@ type VMAuthSpec struct { Ingress *EmbeddedIngress `json:"ingress,omitempty"` // HTTPRoute enables httproute configuration for VMAuth. HTTPRoute *EmbeddedHTTPRoute `json:"httpRoute,omitempty"` - // LivenessProbe that will be added to VMAuth pod - *EmbeddedProbes `json:",inline"` // UnauthorizedAccessConfig configures access for un authorized users // +kubebuilder:validation:Schemaless // +kubebuilder:pruning:PreserveUnknownFields @@ -114,9 +112,8 @@ type VMAuthSpec struct { // +optional ServiceAccountName string `json:"serviceAccountName,omitempty" yaml:"serviceAccountName,omitempty"` - CommonDefaultableParams `json:",inline,omitempty" yaml:",inline"` - CommonConfigReloaderParams `json:",inline,omitempty" yaml:",inline"` - CommonApplicationDeploymentParams `json:",inline,omitempty" yaml:",inline"` + CommonConfigReloaderParams `json:",inline,omitempty" yaml:",inline"` + CommonAppsParams `json:",inline,omitempty" yaml:",inline"` // InternalListenPort instructs vmauth to serve internal routes at given port // available from v0.56.0 operator // and v1.111.0 vmauth version @@ -580,10 +577,6 @@ func (cr *VMAuth) GetStatus() *VMAuthStatus { func (cr *VMAuth) DefaultStatusFields(vs *VMAuthStatus) { } -func (cr *VMAuth) Probe() *EmbeddedProbes { - return cr.Spec.EmbeddedProbes -} - func (cr *VMAuth) ProbePath() string { return BuildPathWithPrefixFlag(cr.Spec.ExtraArgs, healthPath) } @@ -730,13 +723,13 @@ func (cr *VMAuth) GetReloaderParams() *CommonConfigReloaderParams { return &cr.Spec.CommonConfigReloaderParams } -// UseProxyProtocol implements reloadable interface +// UseProxyProtocol implements build.probeCRD interface func (cr *VMAuth) UseProxyProtocol() bool { hasInternalPorts := len(cr.Spec.InternalListenPort) == 0 if cr.Spec.UseProxyProtocol { return hasInternalPorts } - if v, ok := cr.Spec.ExtraArgs["httpListenAddr.useProxyProtocol"]; ok && v == "true" { + if UseProxyProtocol(cr.Spec.ExtraArgs) { return hasInternalPorts } return false diff --git a/api/operator/v1beta1/vmcluster_types.go b/api/operator/v1beta1/vmcluster_types.go index 41ba43212..3903ca6ec 100644 --- a/api/operator/v1beta1/vmcluster_types.go +++ b/api/operator/v1beta1/vmcluster_types.go @@ -311,7 +311,6 @@ type VMSelect struct { // PodDisruptionBudget created by operator // +optional PodDisruptionBudget *EmbeddedPodDisruptionBudgetSpec `json:"podDisruptionBudget,omitempty"` - *EmbeddedProbes `json:",inline"` // Configures horizontal pod autoscaling. // Note, enabling this option disables vmselect to vmselect communication. In most cases it's not an issue. // +optional @@ -333,8 +332,7 @@ type VMSelect struct { // ClaimTemplates allows adding additional VolumeClaimTemplates for StatefulSet ClaimTemplates []corev1.PersistentVolumeClaim `json:"claimTemplates,omitempty"` - CommonDefaultableParams `json:",inline"` - CommonApplicationDeploymentParams `json:",inline"` + CommonAppsParams `json:",inline"` } type InsertPorts struct { @@ -390,25 +388,24 @@ type VMInsert struct { // PodDisruptionBudget created by operator // +optional PodDisruptionBudget *EmbeddedPodDisruptionBudgetSpec `json:"podDisruptionBudget,omitempty"` - *EmbeddedProbes `json:",inline"` // HPA defines kubernetes PodAutoScaling configuration version 2. HPA *EmbeddedHPA `json:"hpa,omitempty"` // Configures vertical pod autoscaling. // +optional VPA *EmbeddedVPA `json:"vpa,omitempty"` - CommonDefaultableParams `json:",inline"` - CommonApplicationDeploymentParams `json:",inline"` -} - -func (cr *VMInsert) Probe() *EmbeddedProbes { - return cr.EmbeddedProbes + CommonAppsParams `json:",inline"` } func (cr *VMInsert) ProbePath() string { return BuildPathWithPrefixFlag(cr.ExtraArgs, healthPath) } +// UseProxyProtocol implements build.probeCRD interface +func (cr *VMInsert) UseProxyProtocol() bool { + return UseProxyProtocol(cr.ExtraArgs) +} + func (cr *VMInsert) ProbeScheme() string { return strings.ToUpper(HTTPProtoFromFlags(cr.ExtraArgs)) } @@ -472,7 +469,6 @@ type VMStorage struct { // PodDisruptionBudget created by operator // +optional PodDisruptionBudget *EmbeddedPodDisruptionBudgetSpec `json:"podDisruptionBudget,omitempty"` - *EmbeddedProbes `json:",inline"` // MaintenanceInsertNodeIDs - excludes given node ids from insert requests routing, must contain pod suffixes - for pod-0, id will be 0 and etc. // lets say, you have pod-0, pod-1, pod-2, pod-3. to exclude pod-0 and pod-3 from insert routing, define nodeIDs: [0,3]. // Useful at storage expanding, when you want to rebalance some data at cluster. @@ -495,8 +491,7 @@ type VMStorage struct { // ClaimTemplates allows adding additional VolumeClaimTemplates for StatefulSet ClaimTemplates []corev1.PersistentVolumeClaim `json:"claimTemplates,omitempty"` - CommonDefaultableParams `json:",inline"` - CommonApplicationDeploymentParams `json:",inline"` + CommonAppsParams `json:",inline"` } type VMBackup struct { @@ -625,6 +620,11 @@ func (cr *VMSelect) GetCacheMountVolumeName() string { return "vmselect-cachedir" } +// UseProxyProtocol implements build.probeCRD interface +func (cr *VMSelect) UseProxyProtocol() bool { + return UseProxyProtocol(cr.ExtraArgs) +} + // GetRemoteWriteURL returns remote write url for VMCluster func (cr *VMCluster) GetRemoteWriteURL() string { if cr == nil || cr.Spec.VMInsert == nil { @@ -827,6 +827,11 @@ func (cr *VMStorage) GetMetricsPath() string { return BuildPathWithPrefixFlag(cr.ExtraArgs, metricsPath) } +// UseProxyProtocol implements build.probeCRD interface +func (cr *VMStorage) UseProxyProtocol() bool { + return UseProxyProtocol(cr.ExtraArgs) +} + // ExtraArgs returns additionally configured command-line arguments func (cr *VMStorage) GetExtraArgs() map[string]string { return cr.ExtraArgs @@ -924,10 +929,6 @@ func (cr *VMCluster) AsURL(kind ClusterComponent) string { return fmt.Sprintf("%s://%s.%s.svc:%s", HTTPProtoFromFlags(extraArgs), cr.PrefixedName(kind), cr.Namespace, port) } -func (cr *VMSelect) Probe() *EmbeddedProbes { - return cr.EmbeddedProbes -} - func (cr *VMSelect) ProbePath() string { return BuildPathWithPrefixFlag(cr.ExtraArgs, healthPath) } @@ -944,10 +945,6 @@ func (*VMSelect) ProbeNeedLiveness() bool { return true } -func (cr *VMStorage) Probe() *EmbeddedProbes { - return cr.EmbeddedProbes -} - func (cr *VMStorage) ProbePath() string { return BuildPathWithPrefixFlag(cr.ExtraArgs, healthPath) } @@ -995,10 +992,8 @@ type VMAuthLoadBalancerSpec struct { // LogLevel for vmauth container. // +optional // +kubebuilder:validation:Enum=INFO;WARN;ERROR;FATAL;PANIC - LogLevel string `json:"logLevel,omitempty"` - CommonApplicationDeploymentParams `json:",inline"` - CommonDefaultableParams `json:",inline"` - *EmbeddedProbes `json:",inline"` + LogLevel string `json:"logLevel,omitempty"` + CommonAppsParams `json:",inline"` // PodDisruptionBudget created by operator // +optional PodDisruptionBudget *EmbeddedPodDisruptionBudgetSpec `json:"podDisruptionBudget,omitempty"` @@ -1018,11 +1013,6 @@ type VMAuthLoadBalancerSpec struct { CommonConfigReloaderParams `json:",inline,omitempty"` } -// ProbePath returns path for probe requests -func (cr *VMAuthLoadBalancerSpec) Probe() *EmbeddedProbes { - return cr.EmbeddedProbes -} - // ProbePort returns port for probe requests func (cr *VMAuthLoadBalancerSpec) ProbePort() string { return cr.Port @@ -1043,6 +1033,11 @@ func (cr *VMAuthLoadBalancerSpec) ProbeScheme() string { return strings.ToUpper(HTTPProtoFromFlags(cr.ExtraArgs)) } +// UseProxyProtocol implements build.probeCRD interface +func (cr *VMAuthLoadBalancerSpec) UseProxyProtocol() bool { + return UseProxyProtocol(cr.ExtraArgs) +} + // GetServiceScrape implements build.serviceScrapeBuilder interface func (cr *VMAuthLoadBalancerSpec) GetServiceScrape() *VMServiceScrapeSpec { return cr.ServiceScrapeSpec diff --git a/api/operator/v1beta1/vmextra_types.go b/api/operator/v1beta1/vmextra_types.go index 477d9793a..2dae14b9e 100644 --- a/api/operator/v1beta1/vmextra_types.go +++ b/api/operator/v1beta1/vmextra_types.go @@ -47,10 +47,11 @@ const ( ) const ( - httpPathPrefixFlag = "http.pathPrefix" - reloadAuthKeyFlag = "reloadAuthKey" - tlsFlag = "tls" - snapshotAuthKeyFlag = "snapshotAuthKey" + httpPathPrefixFlag = "http.pathPrefix" + httpUseProxyProtocolFlag = "httpListenAddr.useProxyProtocol" + reloadAuthKeyFlag = "reloadAuthKey" + tlsFlag = "tls" + snapshotAuthKeyFlag = "snapshotAuthKey" healthPath = "/health" metricsPath = "/metrics" @@ -131,6 +132,17 @@ func ClusterPrefixedName(kind ClusterComponent, name, prefix string, internal bo return fmt.Sprintf("%s%s%s-%s", prefix, string(kind), suffix, name) } +// UseProxyProtocol is a helper for build.probeCRD interface implementations +func UseProxyProtocol(extraArgs map[string]string) bool { + if v, ok := extraArgs[httpUseProxyProtocolFlag]; ok { + if idx := strings.Index(v, ","); idx != -1 { + v = v[:idx] + } + return strings.TrimSpace(v) == "true" + } + return false +} + // EmbeddedObjectMetadata contains a subset of the fields included in k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta // Only fields which are relevant to embedded resources are included. type EmbeddedObjectMetadata struct { @@ -397,21 +409,6 @@ func (epdbs *EmbeddedPodDisruptionBudgetSpec) SelectorLabelsWithDefaults(default return epdbs.SelectorLabels } -// EmbeddedProbes - it allows to override some probe params. -// its not necessary to specify all options, -// operator will replace missing spec with default values. -type EmbeddedProbes struct { - // LivenessProbe that will be added CRD pod - // +optional - LivenessProbe *corev1.Probe `json:"livenessProbe,omitempty"` - // ReadinessProbe that will be added CRD pod - // +optional - ReadinessProbe *corev1.Probe `json:"readinessProbe,omitempty"` - // StartupProbe that will be added to CRD pod - // +optional - StartupProbe *corev1.Probe `json:"startupProbe,omitempty"` -} - // EmbeddedHPA embeds HorizontalPodAutoScaler spec v2. // https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/horizontal-pod-autoscaler-v2/ type EmbeddedHPA struct { @@ -1102,38 +1099,6 @@ func (s *StatefulSetUpdateStrategyBehavior) Validate() error { return nil } -// CommonDefaultableParams contains Application settings -// with known values populated from operator configuration -type CommonDefaultableParams struct { - // Image - docker image settings - // if no specified operator uses default version from operator config - // +optional - Image Image `json:"image,omitempty"` - // Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ - // if not defined default resources from operator config will be used - // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Resources",xDescriptors="urn:alm:descriptor:com.tectonic.ui:resourceRequirements" - // +optional - Resources corev1.ResourceRequirements `json:"resources,omitempty"` - // UseDefaultResources controls resource settings - // By default, operator sets built-in resource requirements - // +optional - UseDefaultResources *bool `json:"useDefaultResources,omitempty"` - // Port listen address - // +optional - Port string `json:"port,omitempty"` - // UseStrictSecurity enables strict security mode for component - // it restricts disk writes access - // uses non-root user out of the box - // drops not needed security permissions - // +optional - UseStrictSecurity *bool `json:"useStrictSecurity,omitempty"` - // DisableSelfServiceScrape controls creation of VMServiceScrape by operator - // for the application. - // Has priority over `VM_DISABLESELFSERVICESCRAPECREATION` operator env variable - // +optional - DisableSelfServiceScrape *bool `json:"disableSelfServiceScrape,omitempty"` -} - type CommonConfigReloaderParams struct { // UseVMConfigReloader replaces prometheus-like config-reloader // with vm one. It uses secrets watch instead of file watch @@ -1164,9 +1129,9 @@ type CommonConfigReloaderParams struct { ConfigReloadAuthKeySecret *corev1.SecretKeySelector `json:"configReloadAuthKeySecret,omitempty"` } -// CommonApplicationDeploymentParams defines common params +// CommonAppsParams defines common params // for deployment and statefulset specifications -type CommonApplicationDeploymentParams struct { +type CommonAppsParams struct { // Affinity If specified, the pod's scheduling constraints. // +optional Affinity *corev1.Affinity `json:"affinity,omitempty"` @@ -1223,9 +1188,6 @@ type CommonApplicationDeploymentParams struct { // see https://kubernetes.io/docs/concepts/containers/images/#referring-to-an-imagepullsecrets-on-a-pod // +optional ImagePullSecrets []corev1.LocalObjectReference `json:"imagePullSecrets,omitempty"` - // TerminationGracePeriodSeconds period for container graceful termination - // +optional - TerminationGracePeriodSeconds *int64 `json:"terminationGracePeriodSeconds,omitempty"` // ReadinessGates defines pod readiness gates ReadinessGates []corev1.PodReadinessGate `json:"readinessGates,omitempty"` // MinReadySeconds defines a minimum number of seconds to wait before starting update next pod @@ -1294,6 +1256,46 @@ type CommonApplicationDeploymentParams struct { // could either be secret or configmap // +optional ExtraEnvsFrom []corev1.EnvFromSource `json:"extraEnvsFrom,omitempty"` + + // Image - docker image settings + // if no specified operator uses default version from operator config + // +optional + Image Image `json:"image,omitempty"` + // Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + // if not defined default resources from operator config will be used + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Resources",xDescriptors="urn:alm:descriptor:com.tectonic.ui:resourceRequirements" + // +optional + Resources corev1.ResourceRequirements `json:"resources,omitempty"` + // UseDefaultResources controls resource settings + // By default, operator sets built-in resource requirements + // +optional + UseDefaultResources *bool `json:"useDefaultResources,omitempty"` + // Port listen address + // +optional + Port string `json:"port,omitempty"` + // UseStrictSecurity enables strict security mode for component + // it restricts disk writes access + // uses non-root user out of the box + // drops not needed security permissions + // +optional + UseStrictSecurity *bool `json:"useStrictSecurity,omitempty"` + // DisableSelfServiceScrape controls creation of VMServiceScrape by operator + // for the application. + // Has priority over `VM_DISABLESELFSERVICESCRAPECREATION` operator env variable + // +optional + DisableSelfServiceScrape *bool `json:"disableSelfServiceScrape,omitempty"` + // TerminationGracePeriodSeconds period for container graceful termination + // +optional + TerminationGracePeriodSeconds *int64 `json:"terminationGracePeriodSeconds,omitempty"` + // LivenessProbe that will be added to CR pod + // +optional + LivenessProbe *corev1.Probe `json:"livenessProbe,omitempty"` + // ReadinessProbe that will be added to CR pod + // +optional + ReadinessProbe *corev1.Probe `json:"readinessProbe,omitempty"` + // StartupProbe that will be added to CR pod + // +optional + StartupProbe *corev1.Probe `json:"startupProbe,omitempty"` } // SecurityContext extends PodSecurityContext with ContainerSecurityContext diff --git a/api/operator/v1beta1/vmextra_types_test.go b/api/operator/v1beta1/vmextra_types_test.go index 1cfa7ae72..bdeb4100a 100644 --- a/api/operator/v1beta1/vmextra_types_test.go +++ b/api/operator/v1beta1/vmextra_types_test.go @@ -126,6 +126,57 @@ func TestStringOrArrayUnMarshal(t *testing.T) { } +func TestUseProxyProtocol(t *testing.T) { + type opts struct { + args map[string]string + expected bool + } + f := func(o opts) { + assert.Equal(t, o.expected, UseProxyProtocol(o.args)) + } + + // no args set + f(opts{}) + + // no proxy protocol flag + f(opts{ + args: map[string]string{ + "test": "test", + }, + }) + + // proxy protocol set to false + f(opts{ + args: map[string]string{ + httpUseProxyProtocolFlag: "false", + }, + }) + + // first proxy protocol value is false + f(opts{ + args: map[string]string{ + httpUseProxyProtocolFlag: "false,true,true", + }, + }) + + // proxy protocol is true + f(opts{ + args: map[string]string{ + httpUseProxyProtocolFlag: "true", + }, + expected: true, + }) + + // only first value is true + f(opts{ + args: map[string]string{ + httpUseProxyProtocolFlag: "true,false,false", + }, + expected: true, + }) + +} + func TestEmbeddedVPAValidation(t *testing.T) { type opts struct { vpa *EmbeddedVPA diff --git a/api/operator/v1beta1/vmsingle_types.go b/api/operator/v1beta1/vmsingle_types.go index 3f1647e40..98183602f 100644 --- a/api/operator/v1beta1/vmsingle_types.go +++ b/api/operator/v1beta1/vmsingle_types.go @@ -75,8 +75,6 @@ type VMSingleSpec struct { // ServiceScrapeSpec that will be added to vmsingle VMServiceScrape spec // +optional ServiceScrapeSpec *VMServiceScrapeSpec `json:"serviceScrapeSpec,omitempty"` - // LivenessProbe that will be added to VMSingle pod - *EmbeddedProbes `json:",inline"` // StreamAggrConfig defines stream aggregation configuration for VMSingle StreamAggrConfig *StreamAggrConfig `json:"streamAggrConfig,omitempty"` @@ -84,8 +82,7 @@ type VMSingleSpec struct { // +optional ServiceAccountName string `json:"serviceAccountName,omitempty"` - CommonDefaultableParams `json:",inline"` - CommonApplicationDeploymentParams `json:",inline"` + CommonAppsParams `json:",inline"` } // HasAnyStreamAggrRule checks if vmsingle has any defined aggregation rules @@ -111,6 +108,16 @@ func (cr *VMSingle) UnmarshalJSON(src []byte) error { return nil } +// UseProxyProtocol implements build.probeCRD interface +func (cr *VMSingle) UseProxyProtocol() bool { + return UseProxyProtocol(cr.Spec.ExtraArgs) +} + +// AutomountServiceAccountToken implements reloadable interface +func (cr *VMSingle) AutomountServiceAccountToken() bool { + return !cr.Spec.DisableAutomountServiceAccountToken +} + // UnmarshalJSON implements json.Unmarshaler interface func (cr *VMSingleSpec) UnmarshalJSON(src []byte) error { type pcr VMSingleSpec @@ -158,10 +165,6 @@ func (cr *VMSingle) GetStatus() *VMSingleStatus { // DefaultStatusFields implements reconcile.ObjectWithDeepCopyAndStatus interface func (cr *VMSingle) DefaultStatusFields(_ *VMSingleStatus) {} -func (cr *VMSingle) Probe() *EmbeddedProbes { - return cr.Spec.EmbeddedProbes -} - func (cr *VMSingle) ProbePath() string { return BuildPathWithPrefixFlag(cr.Spec.ExtraArgs, healthPath) } diff --git a/api/operator/v1beta1/zz_generated.deepcopy.go b/api/operator/v1beta1/zz_generated.deepcopy.go index 52307e9bb..0470edbbf 100644 --- a/api/operator/v1beta1/zz_generated.deepcopy.go +++ b/api/operator/v1beta1/zz_generated.deepcopy.go @@ -284,7 +284,7 @@ func (in *Certs) DeepCopy() *Certs { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CommonApplicationDeploymentParams) DeepCopyInto(out *CommonApplicationDeploymentParams) { +func (in *CommonAppsParams) DeepCopyInto(out *CommonAppsParams) { *out = *in if in.Affinity != nil { in, out := &in.Affinity, &out.Affinity @@ -346,11 +346,6 @@ func (in *CommonApplicationDeploymentParams) DeepCopyInto(out *CommonApplication *out = make([]v1.LocalObjectReference, len(*in)) copy(*out, *in) } - if in.TerminationGracePeriodSeconds != nil { - in, out := &in.TerminationGracePeriodSeconds, &out.TerminationGracePeriodSeconds - *out = new(int64) - **out = **in - } if in.ReadinessGates != nil { in, out := &in.ReadinessGates, &out.ReadinessGates *out = make([]v1.PodReadinessGate, len(*in)) @@ -425,14 +420,51 @@ func (in *CommonApplicationDeploymentParams) DeepCopyInto(out *CommonApplication (*in)[i].DeepCopyInto(&(*out)[i]) } } + out.Image = in.Image + in.Resources.DeepCopyInto(&out.Resources) + if in.UseDefaultResources != nil { + in, out := &in.UseDefaultResources, &out.UseDefaultResources + *out = new(bool) + **out = **in + } + if in.UseStrictSecurity != nil { + in, out := &in.UseStrictSecurity, &out.UseStrictSecurity + *out = new(bool) + **out = **in + } + if in.DisableSelfServiceScrape != nil { + in, out := &in.DisableSelfServiceScrape, &out.DisableSelfServiceScrape + *out = new(bool) + **out = **in + } + if in.TerminationGracePeriodSeconds != nil { + in, out := &in.TerminationGracePeriodSeconds, &out.TerminationGracePeriodSeconds + *out = new(int64) + **out = **in + } + if in.LivenessProbe != nil { + in, out := &in.LivenessProbe, &out.LivenessProbe + *out = new(v1.Probe) + (*in).DeepCopyInto(*out) + } + if in.ReadinessProbe != nil { + in, out := &in.ReadinessProbe, &out.ReadinessProbe + *out = new(v1.Probe) + (*in).DeepCopyInto(*out) + } + if in.StartupProbe != nil { + in, out := &in.StartupProbe, &out.StartupProbe + *out = new(v1.Probe) + (*in).DeepCopyInto(*out) + } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CommonApplicationDeploymentParams. -func (in *CommonApplicationDeploymentParams) DeepCopy() *CommonApplicationDeploymentParams { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CommonAppsParams. +func (in *CommonAppsParams) DeepCopy() *CommonAppsParams { if in == nil { return nil } - out := new(CommonApplicationDeploymentParams) + out := new(CommonAppsParams) in.DeepCopyInto(out) return out } @@ -470,38 +502,6 @@ func (in *CommonConfigReloaderParams) DeepCopy() *CommonConfigReloaderParams { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CommonDefaultableParams) DeepCopyInto(out *CommonDefaultableParams) { - *out = *in - out.Image = in.Image - in.Resources.DeepCopyInto(&out.Resources) - if in.UseDefaultResources != nil { - in, out := &in.UseDefaultResources, &out.UseDefaultResources - *out = new(bool) - **out = **in - } - if in.UseStrictSecurity != nil { - in, out := &in.UseStrictSecurity, &out.UseStrictSecurity - *out = new(bool) - **out = **in - } - if in.DisableSelfServiceScrape != nil { - in, out := &in.DisableSelfServiceScrape, &out.DisableSelfServiceScrape - *out = new(bool) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CommonDefaultableParams. -func (in *CommonDefaultableParams) DeepCopy() *CommonDefaultableParams { - if in == nil { - return nil - } - out := new(CommonDefaultableParams) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *CommonRelabelParams) DeepCopyInto(out *CommonRelabelParams) { *out = *in @@ -1382,36 +1382,6 @@ func (in *EmbeddedPodDisruptionBudgetSpec) DeepCopy() *EmbeddedPodDisruptionBudg return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *EmbeddedProbes) DeepCopyInto(out *EmbeddedProbes) { - *out = *in - if in.LivenessProbe != nil { - in, out := &in.LivenessProbe, &out.LivenessProbe - *out = new(v1.Probe) - (*in).DeepCopyInto(*out) - } - if in.ReadinessProbe != nil { - in, out := &in.ReadinessProbe, &out.ReadinessProbe - *out = new(v1.Probe) - (*in).DeepCopyInto(*out) - } - if in.StartupProbe != nil { - in, out := &in.StartupProbe, &out.StartupProbe - *out = new(v1.Probe) - (*in).DeepCopyInto(*out) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EmbeddedProbes. -func (in *EmbeddedProbes) DeepCopy() *EmbeddedProbes { - if in == nil { - return nil - } - out := new(EmbeddedProbes) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *EmbeddedVPA) DeepCopyInto(out *EmbeddedVPA) { *out = *in @@ -4098,8 +4068,7 @@ func (in *VLogsSpec) DeepCopyInto(out *VLogsSpec) { *out = new(ManagedObjectsMetadata) (*in).DeepCopyInto(*out) } - in.CommonDefaultableParams.DeepCopyInto(&out.CommonDefaultableParams) - in.CommonApplicationDeploymentParams.DeepCopyInto(&out.CommonApplicationDeploymentParams) + in.CommonAppsParams.DeepCopyInto(&out.CommonAppsParams) if in.Storage != nil { in, out := &in.Storage, &out.Storage *out = new(v1.PersistentVolumeClaimSpec) @@ -4116,11 +4085,6 @@ func (in *VLogsSpec) DeepCopyInto(out *VLogsSpec) { *out = new(VMServiceScrapeSpec) (*in).DeepCopyInto(*out) } - if in.EmbeddedProbes != nil { - in, out := &in.EmbeddedProbes, &out.EmbeddedProbes - *out = new(EmbeddedProbes) - (*in).DeepCopyInto(*out) - } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VLogsSpec. @@ -4416,11 +4380,6 @@ func (in *VMAgentSpec) DeepCopyInto(out *VMAgentSpec) { *out = new(EmbeddedPodDisruptionBudgetSpec) (*in).DeepCopyInto(*out) } - if in.EmbeddedProbes != nil { - in, out := &in.EmbeddedProbes, &out.EmbeddedProbes - *out = new(EmbeddedProbes) - (*in).DeepCopyInto(*out) - } if in.StatefulStorage != nil { in, out := &in.StatefulStorage, &out.StatefulStorage *out = new(StorageSpec) @@ -4445,9 +4404,8 @@ func (in *VMAgentSpec) DeepCopyInto(out *VMAgentSpec) { } in.CommonRelabelParams.DeepCopyInto(&out.CommonRelabelParams) in.CommonScrapeParams.DeepCopyInto(&out.CommonScrapeParams) - in.CommonDefaultableParams.DeepCopyInto(&out.CommonDefaultableParams) in.CommonConfigReloaderParams.DeepCopyInto(&out.CommonConfigReloaderParams) - in.CommonApplicationDeploymentParams.DeepCopyInto(&out.CommonApplicationDeploymentParams) + in.CommonAppsParams.DeepCopyInto(&out.CommonAppsParams) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VMAgentSpec. @@ -4722,19 +4680,13 @@ func (in *VMAlertSpec) DeepCopyInto(out *VMAlertSpec) { *out = new(EmbeddedPodDisruptionBudgetSpec) (*in).DeepCopyInto(*out) } - if in.EmbeddedProbes != nil { - in, out := &in.EmbeddedProbes, &out.EmbeddedProbes - *out = new(EmbeddedProbes) - (*in).DeepCopyInto(*out) - } if in.License != nil { in, out := &in.License, &out.License *out = new(License) (*in).DeepCopyInto(*out) } - in.CommonDefaultableParams.DeepCopyInto(&out.CommonDefaultableParams) in.CommonConfigReloaderParams.DeepCopyInto(&out.CommonConfigReloaderParams) - in.CommonApplicationDeploymentParams.DeepCopyInto(&out.CommonApplicationDeploymentParams) + in.CommonAppsParams.DeepCopyInto(&out.CommonAppsParams) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VMAlertSpec. @@ -5038,11 +4990,6 @@ func (in *VMAlertmanagerSpec) DeepCopyInto(out *VMAlertmanagerSpec) { *out = new(EmbeddedPodDisruptionBudgetSpec) (*in).DeepCopyInto(*out) } - if in.EmbeddedProbes != nil { - in, out := &in.EmbeddedProbes, &out.EmbeddedProbes - *out = new(EmbeddedProbes) - (*in).DeepCopyInto(*out) - } if in.ConfigSelector != nil { in, out := &in.ConfigSelector, &out.ConfigSelector *out = new(metav1.LabelSelector) @@ -5080,9 +5027,8 @@ func (in *VMAlertmanagerSpec) DeepCopyInto(out *VMAlertmanagerSpec) { *out = new(VMAlertmanagerTracingConfig) (*in).DeepCopyInto(*out) } - in.CommonDefaultableParams.DeepCopyInto(&out.CommonDefaultableParams) in.CommonConfigReloaderParams.DeepCopyInto(&out.CommonConfigReloaderParams) - in.CommonApplicationDeploymentParams.DeepCopyInto(&out.CommonApplicationDeploymentParams) + in.CommonAppsParams.DeepCopyInto(&out.CommonAppsParams) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VMAlertmanagerSpec. @@ -5268,13 +5214,7 @@ func (in *VMAuthLoadBalancerSpec) DeepCopyInto(out *VMAuthLoadBalancerSpec) { *out = new(VMServiceScrapeSpec) (*in).DeepCopyInto(*out) } - in.CommonApplicationDeploymentParams.DeepCopyInto(&out.CommonApplicationDeploymentParams) - in.CommonDefaultableParams.DeepCopyInto(&out.CommonDefaultableParams) - if in.EmbeddedProbes != nil { - in, out := &in.EmbeddedProbes, &out.EmbeddedProbes - *out = new(EmbeddedProbes) - (*in).DeepCopyInto(*out) - } + in.CommonAppsParams.DeepCopyInto(&out.CommonAppsParams) if in.PodDisruptionBudget != nil { in, out := &in.PodDisruptionBudget, &out.PodDisruptionBudget *out = new(EmbeddedPodDisruptionBudgetSpec) @@ -5356,11 +5296,6 @@ func (in *VMAuthSpec) DeepCopyInto(out *VMAuthSpec) { *out = new(EmbeddedHTTPRoute) (*in).DeepCopyInto(*out) } - if in.EmbeddedProbes != nil { - in, out := &in.EmbeddedProbes, &out.EmbeddedProbes - *out = new(EmbeddedProbes) - (*in).DeepCopyInto(*out) - } if in.UnauthorizedAccessConfig != nil { in, out := &in.UnauthorizedAccessConfig, &out.UnauthorizedAccessConfig *out = make([]UnauthorizedAccessConfigURLMap, len(*in)) @@ -5380,9 +5315,8 @@ func (in *VMAuthSpec) DeepCopyInto(out *VMAuthSpec) { (*in).DeepCopyInto(*out) } in.ExternalConfig.DeepCopyInto(&out.ExternalConfig) - in.CommonDefaultableParams.DeepCopyInto(&out.CommonDefaultableParams) in.CommonConfigReloaderParams.DeepCopyInto(&out.CommonConfigReloaderParams) - in.CommonApplicationDeploymentParams.DeepCopyInto(&out.CommonApplicationDeploymentParams) + in.CommonAppsParams.DeepCopyInto(&out.CommonAppsParams) if in.UpdateStrategy != nil { in, out := &in.UpdateStrategy, &out.UpdateStrategy *out = new(appsv1.DeploymentStrategyType) @@ -5742,11 +5676,6 @@ func (in *VMInsert) DeepCopyInto(out *VMInsert) { *out = new(EmbeddedPodDisruptionBudgetSpec) (*in).DeepCopyInto(*out) } - if in.EmbeddedProbes != nil { - in, out := &in.EmbeddedProbes, &out.EmbeddedProbes - *out = new(EmbeddedProbes) - (*in).DeepCopyInto(*out) - } if in.HPA != nil { in, out := &in.HPA, &out.HPA *out = new(EmbeddedHPA) @@ -5757,8 +5686,7 @@ func (in *VMInsert) DeepCopyInto(out *VMInsert) { *out = new(EmbeddedVPA) (*in).DeepCopyInto(*out) } - in.CommonDefaultableParams.DeepCopyInto(&out.CommonDefaultableParams) - in.CommonApplicationDeploymentParams.DeepCopyInto(&out.CommonApplicationDeploymentParams) + in.CommonAppsParams.DeepCopyInto(&out.CommonAppsParams) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VMInsert. @@ -6555,11 +6483,6 @@ func (in *VMSelect) DeepCopyInto(out *VMSelect) { *out = new(EmbeddedPodDisruptionBudgetSpec) (*in).DeepCopyInto(*out) } - if in.EmbeddedProbes != nil { - in, out := &in.EmbeddedProbes, &out.EmbeddedProbes - *out = new(EmbeddedProbes) - (*in).DeepCopyInto(*out) - } if in.HPA != nil { in, out := &in.HPA, &out.HPA *out = new(EmbeddedHPA) @@ -6582,8 +6505,7 @@ func (in *VMSelect) DeepCopyInto(out *VMSelect) { (*in)[i].DeepCopyInto(&(*out)[i]) } } - in.CommonDefaultableParams.DeepCopyInto(&out.CommonDefaultableParams) - in.CommonApplicationDeploymentParams.DeepCopyInto(&out.CommonApplicationDeploymentParams) + in.CommonAppsParams.DeepCopyInto(&out.CommonAppsParams) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VMSelect. @@ -6803,18 +6725,12 @@ func (in *VMSingleSpec) DeepCopyInto(out *VMSingleSpec) { *out = new(VMServiceScrapeSpec) (*in).DeepCopyInto(*out) } - if in.EmbeddedProbes != nil { - in, out := &in.EmbeddedProbes, &out.EmbeddedProbes - *out = new(EmbeddedProbes) - (*in).DeepCopyInto(*out) - } if in.StreamAggrConfig != nil { in, out := &in.StreamAggrConfig, &out.StreamAggrConfig *out = new(StreamAggrConfig) (*in).DeepCopyInto(*out) } - in.CommonDefaultableParams.DeepCopyInto(&out.CommonDefaultableParams) - in.CommonApplicationDeploymentParams.DeepCopyInto(&out.CommonApplicationDeploymentParams) + in.CommonAppsParams.DeepCopyInto(&out.CommonAppsParams) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VMSingleSpec. @@ -6981,11 +6897,6 @@ func (in *VMStorage) DeepCopyInto(out *VMStorage) { *out = new(EmbeddedPodDisruptionBudgetSpec) (*in).DeepCopyInto(*out) } - if in.EmbeddedProbes != nil { - in, out := &in.EmbeddedProbes, &out.EmbeddedProbes - *out = new(EmbeddedProbes) - (*in).DeepCopyInto(*out) - } if in.MaintenanceInsertNodeIDs != nil { in, out := &in.MaintenanceInsertNodeIDs, &out.MaintenanceInsertNodeIDs *out = make([]int32, len(*in)) @@ -7008,8 +6919,7 @@ func (in *VMStorage) DeepCopyInto(out *VMStorage) { (*in)[i].DeepCopyInto(&(*out)[i]) } } - in.CommonDefaultableParams.DeepCopyInto(&out.CommonDefaultableParams) - in.CommonApplicationDeploymentParams.DeepCopyInto(&out.CommonApplicationDeploymentParams) + in.CommonAppsParams.DeepCopyInto(&out.CommonAppsParams) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VMStorage. diff --git a/config/crd/overlay/crd.yaml b/config/crd/overlay/crd.yaml index 29aee3e31..806f99e63 100644 --- a/config/crd/overlay/crd.yaml +++ b/config/crd/overlay/crd.yaml @@ -860,7 +860,7 @@ spec: type: string type: object livenessProbe: - description: LivenessProbe that will be added CRD pod + description: LivenessProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true logFormat: @@ -1036,7 +1036,7 @@ spec: type: object type: array readinessProbe: - description: ReadinessProbe that will be added CRD pod + description: ReadinessProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true remoteWrite: @@ -1476,7 +1476,7 @@ spec: - spec type: object startupProbe: - description: StartupProbe that will be added to CRD pod + description: StartupProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true storage: @@ -2829,7 +2829,7 @@ spec: x-kubernetes-preserve-unknown-fields: true type: array livenessProbe: - description: LivenessProbe that will be added CRD pod + description: LivenessProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true logFormat: @@ -2963,7 +2963,7 @@ spec: type: object type: array readinessProbe: - description: ReadinessProbe that will be added CRD pod + description: ReadinessProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true replicaCount: @@ -3156,7 +3156,7 @@ spec: - spec type: object startupProbe: - description: StartupProbe that will be added to CRD pod + description: StartupProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true syslogSpec: @@ -3913,7 +3913,7 @@ spec: x-kubernetes-preserve-unknown-fields: true type: array livenessProbe: - description: LivenessProbe that will be added CRD pod + description: LivenessProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true logFormat: @@ -4047,7 +4047,7 @@ spec: type: object type: array readinessProbe: - description: ReadinessProbe that will be added CRD pod + description: ReadinessProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true replicaCount: @@ -4240,7 +4240,7 @@ spec: - spec type: object startupProbe: - description: StartupProbe that will be added to CRD pod + description: StartupProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true terminationGracePeriodSeconds: @@ -5472,7 +5472,7 @@ spec: x-kubernetes-preserve-unknown-fields: true type: array livenessProbe: - description: LivenessProbe that will be added CRD pod + description: LivenessProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true logFormat: @@ -5652,7 +5652,7 @@ spec: type: object type: array readinessProbe: - description: ReadinessProbe that will be added CRD pod + description: ReadinessProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true replicaCount: @@ -5843,7 +5843,7 @@ spec: - spec type: object startupProbe: - description: StartupProbe that will be added to CRD pod + description: StartupProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true storage: @@ -6780,7 +6780,7 @@ spec: type: string type: object livenessProbe: - description: LivenessProbe that will be added CRD pod + description: LivenessProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true logFormat: @@ -6900,7 +6900,7 @@ spec: type: object type: array readinessProbe: - description: ReadinessProbe that will be added CRD pod + description: ReadinessProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true replicaCount: @@ -7073,7 +7073,7 @@ spec: - spec type: object startupProbe: - description: StartupProbe that will be added to CRD pod + description: StartupProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true storage: @@ -9201,7 +9201,7 @@ spec: type: string type: object livenessProbe: - description: LivenessProbe that will be added CRD pod + description: LivenessProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true logFormat: @@ -9936,7 +9936,7 @@ spec: type: object type: array readinessProbe: - description: ReadinessProbe that will be added CRD pod + description: ReadinessProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true relabelConfig: @@ -12043,7 +12043,7 @@ spec: see [here](https://docs.victoriametrics.com/victoriametrics/vmagent/#scraping-big-number-of-targets) type: integer startupProbe: - description: StartupProbe that will be added to CRD pod + description: StartupProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true statefulMode: @@ -18953,7 +18953,7 @@ spec: UI, not the gossip communication. type: boolean livenessProbe: - description: LivenessProbe that will be added CRD pod + description: LivenessProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true logFormat: @@ -19135,7 +19135,7 @@ spec: type: object type: array readinessProbe: - description: ReadinessProbe that will be added CRD pod + description: ReadinessProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true replicaCount: @@ -19321,7 +19321,7 @@ spec: - spec type: object startupProbe: - description: StartupProbe that will be added to CRD pod + description: StartupProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true storage: @@ -20988,7 +20988,7 @@ spec: type: string type: object livenessProbe: - description: LivenessProbe that will be added CRD pod + description: LivenessProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true logFormat: @@ -21557,7 +21557,7 @@ spec: type: object type: array readinessProbe: - description: ReadinessProbe that will be added CRD pod + description: ReadinessProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true remoteRead: @@ -22147,7 +22147,7 @@ spec: - spec type: object startupProbe: - description: StartupProbe that will be added to CRD pod + description: StartupProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true terminationGracePeriodSeconds: @@ -23219,7 +23219,7 @@ spec: type: string type: object livenessProbe: - description: LivenessProbe that will be added CRD pod + description: LivenessProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true logLevel: @@ -23994,7 +23994,7 @@ spec: type: object type: array readinessProbe: - description: ReadinessProbe that will be added CRD pod + description: ReadinessProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true replicaCount: @@ -24140,7 +24140,7 @@ spec: replicas count according to spec.replicas. type: integer startupProbe: - description: StartupProbe that will be added to CRD pod + description: StartupProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true storage: @@ -26750,7 +26750,7 @@ spec: type: string type: object livenessProbe: - description: LivenessProbe that will be added CRD pod + description: LivenessProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true logFormat: @@ -26905,7 +26905,7 @@ spec: type: object type: array readinessProbe: - description: ReadinessProbe that will be added CRD pod + description: ReadinessProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true replicaCount: @@ -27110,7 +27110,7 @@ spec: - spec type: object startupProbe: - description: StartupProbe that will be added to CRD pod + description: StartupProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true terminationGracePeriodSeconds: @@ -28689,7 +28689,7 @@ spec: type: string type: object livenessProbe: - description: LivenessProbe that will be added CRD pod + description: LivenessProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true logFormat: @@ -28823,7 +28823,7 @@ spec: type: object type: array readinessProbe: - description: ReadinessProbe that will be added CRD pod + description: ReadinessProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true replicaCount: @@ -29016,7 +29016,7 @@ spec: - spec type: object startupProbe: - description: StartupProbe that will be added to CRD pod + description: StartupProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true terminationGracePeriodSeconds: @@ -29626,7 +29626,7 @@ spec: x-kubernetes-preserve-unknown-fields: true type: array livenessProbe: - description: LivenessProbe that will be added CRD pod + description: LivenessProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true logFormat: @@ -29780,7 +29780,7 @@ spec: type: object type: array readinessProbe: - description: ReadinessProbe that will be added CRD pod + description: ReadinessProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true replicaCount: @@ -29957,7 +29957,7 @@ spec: - spec type: object startupProbe: - description: StartupProbe that will be added to CRD pod + description: StartupProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true storage: @@ -31653,7 +31653,7 @@ spec: x-kubernetes-preserve-unknown-fields: true type: array livenessProbe: - description: LivenessProbe that will be added CRD pod + description: LivenessProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true logFormat: @@ -31824,7 +31824,7 @@ spec: type: object type: array readinessProbe: - description: ReadinessProbe that will be added CRD pod + description: ReadinessProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true replicaCount: @@ -32001,7 +32001,7 @@ spec: - spec type: object startupProbe: - description: StartupProbe that will be added to CRD pod + description: StartupProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true storage: @@ -38690,7 +38690,7 @@ spec: type: string type: object livenessProbe: - description: LivenessProbe that will be added CRD pod + description: LivenessProbe that will be added to CR pod properties: exec: description: Exec specifies a command to execute in the @@ -38996,7 +38996,7 @@ spec: type: object type: array readinessProbe: - description: ReadinessProbe that will be added CRD pod + description: ReadinessProbe that will be added to CR pod properties: exec: description: Exec specifies a command to execute in the @@ -40881,7 +40881,7 @@ spec: - spec type: object startupProbe: - description: StartupProbe that will be added to CRD pod + description: StartupProbe that will be added to CR pod properties: exec: description: Exec specifies a command to execute in the @@ -48981,7 +48981,7 @@ spec: type: string type: object livenessProbe: - description: LivenessProbe that will be added CRD pod + description: LivenessProbe that will be added to CR pod properties: exec: description: Exec specifies a command to execute in @@ -49308,7 +49308,7 @@ spec: type: object type: array readinessProbe: - description: ReadinessProbe that will be added CRD pod + description: ReadinessProbe that will be added to CR pod properties: exec: description: Exec specifies a command to execute in @@ -49929,7 +49929,7 @@ spec: to use to run the pods type: string startupProbe: - description: StartupProbe that will be added to CRD pod + description: StartupProbe that will be added to CR pod properties: exec: description: Exec specifies a command to execute in @@ -57769,7 +57769,8 @@ spec: type: string type: object livenessProbe: - description: LivenessProbe that will be added CRD pod + description: LivenessProbe that will be added to CR + pod properties: exec: description: Exec specifies a command to execute @@ -58098,7 +58099,8 @@ spec: type: object type: array readinessProbe: - description: ReadinessProbe that will be added CRD pod + description: ReadinessProbe that will be added to CR + pod properties: exec: description: Exec specifies a command to execute @@ -58720,8 +58722,7 @@ spec: to use to run the pods type: string startupProbe: - description: StartupProbe that will be added to CRD - pod + description: StartupProbe that will be added to CR pod properties: exec: description: Exec specifies a command to execute @@ -70522,7 +70523,7 @@ spec: type: string type: object livenessProbe: - description: LivenessProbe that will be added CRD pod + description: LivenessProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true logFormat: @@ -70634,7 +70635,7 @@ spec: type: object type: array readinessProbe: - description: ReadinessProbe that will be added CRD pod + description: ReadinessProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true removePvcAfterDelete: @@ -70807,7 +70808,7 @@ spec: - spec type: object startupProbe: - description: StartupProbe that will be added to CRD pod + description: StartupProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true storage: @@ -74045,7 +74046,7 @@ spec: x-kubernetes-preserve-unknown-fields: true type: array livenessProbe: - description: LivenessProbe that will be added CRD pod + description: LivenessProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true logFormat: @@ -74179,7 +74180,7 @@ spec: type: object type: array readinessProbe: - description: ReadinessProbe that will be added CRD pod + description: ReadinessProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true replicaCount: @@ -74372,7 +74373,7 @@ spec: - spec type: object startupProbe: - description: StartupProbe that will be added to CRD pod + description: StartupProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true terminationGracePeriodSeconds: @@ -75023,7 +75024,7 @@ spec: x-kubernetes-preserve-unknown-fields: true type: array livenessProbe: - description: LivenessProbe that will be added CRD pod + description: LivenessProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true logFormat: @@ -75157,7 +75158,7 @@ spec: type: object type: array readinessProbe: - description: ReadinessProbe that will be added CRD pod + description: ReadinessProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true replicaCount: @@ -75350,7 +75351,7 @@ spec: - spec type: object startupProbe: - description: StartupProbe that will be added to CRD pod + description: StartupProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true terminationGracePeriodSeconds: @@ -76588,7 +76589,7 @@ spec: x-kubernetes-preserve-unknown-fields: true type: array livenessProbe: - description: LivenessProbe that will be added CRD pod + description: LivenessProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true logFormat: @@ -76769,7 +76770,7 @@ spec: type: object type: array readinessProbe: - description: ReadinessProbe that will be added CRD pod + description: ReadinessProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true replicaCount: @@ -76960,7 +76961,7 @@ spec: - spec type: object startupProbe: - description: StartupProbe that will be added to CRD pod + description: StartupProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true storage: @@ -77722,7 +77723,7 @@ spec: x-kubernetes-preserve-unknown-fields: true type: array livenessProbe: - description: LivenessProbe that will be added CRD pod + description: LivenessProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true logFormat: @@ -77843,7 +77844,7 @@ spec: type: object type: array readinessProbe: - description: ReadinessProbe that will be added CRD pod + description: ReadinessProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true replicaCount: @@ -78016,7 +78017,7 @@ spec: - spec type: object startupProbe: - description: StartupProbe that will be added to CRD pod + description: StartupProbe that will be added to CR pod type: object x-kubernetes-preserve-unknown-fields: true storage: diff --git a/docs/api.md b/docs/api.md index 93eab4967..ee16f47d1 100644 --- a/docs/api.md +++ b/docs/api.md @@ -225,6 +225,7 @@ Appears in: [VLAgent](#vlagent) | initContainers#
_[Container](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#container-v1-core) array_ | _(Optional)_
InitContainers allows adding initContainers to the pod definition.
Any errors during the execution of an initContainer will lead to a restart of the Pod.
More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ | | k8sCollector#
_[VLAgentK8sCollector](#vlagentk8scollector)_ | _(Required)_
K8sCollector configures VLAgent logs collection from K8s pods | | license#
_[License](#license)_ | _(Optional)_
License allows to configure license key to be used for enterprise features.
See [here](https://docs.victoriametrics.com/victoriametrics/enterprise/#victorialogs-enterprise-features) | +| livenessProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
LivenessProbe that will be added to CR pod | | logFormat#
_string_ | _(Optional)_
LogFormat for VLAgent to be configured with. | | logLevel#
_string_ | _(Optional)_
LogLevel for VLAgent to be configured with.
INFO, WARN, ERROR, FATAL, PANIC | | managedMetadata#
_[ManagedObjectsMetadata](#managedobjectsmetadata)_ | _(Required)_
ManagedMetadata defines metadata that will be added to the all objects
created by operator for the given CustomResource | @@ -237,6 +238,7 @@ Appears in: [VLAgent](#vlagent) | port#
_string_ | _(Optional)_
Port listen address | | priorityClassName#
_string_ | _(Optional)_
PriorityClassName class assigned to the Pods | | readinessGates#
_[PodReadinessGate](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#podreadinessgate-v1-core) array_ | _(Required)_
ReadinessGates defines pod readiness gates | +| readinessProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
ReadinessProbe that will be added to CR pod | | remoteWrite#
_[VLAgentRemoteWriteSpec](#vlagentremotewritespec) array_ | _(Required)_
RemoteWrite list of victoria logs endpoints
for victorialogs it must looks like: http://victoria-logs-single:9428/
or for cluster different url
https://docs.victoriametrics.com/victorialogs/vlagent/#replication-and-high-availability | | remoteWriteSettings#
_[VLAgentRemoteWriteSettings](#vlagentremotewritesettings)_ | _(Optional)_
RemoteWriteSettings defines global settings for all remoteWrite urls. | | replicaCount#
_integer_ | _(Optional)_
ReplicaCount is the expected size of the Application. | @@ -250,6 +252,7 @@ Appears in: [VLAgent](#vlagent) | serviceAccountName#
_string_ | _(Optional)_
ServiceAccountName is the name of the ServiceAccount to use to run the pods | | serviceScrapeSpec#
_[VMServiceScrapeSpec](#vmservicescrapespec)_ | _(Optional)_
ServiceScrapeSpec that will be added to vlagent VMServiceScrape spec | | serviceSpec#
_[AdditionalServiceSpec](#additionalservicespec)_ | _(Optional)_
ServiceSpec that will be added to vlagent service spec | +| startupProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
StartupProbe that will be added to CR pod | | storage#
_[StorageSpec](#storagespec)_ | _(Optional)_
Storage configures storage for StatefulSet | | syslogSpec#
_[SyslogServerSpec](#syslogserverspec)_ | _(Optional)_
SyslogSpec defines syslog listener configuration | | terminationGracePeriodSeconds#
_integer_ | _(Optional)_
TerminationGracePeriodSeconds period for container graceful termination | @@ -318,6 +321,7 @@ Appears in: [VLClusterSpec](#vlclusterspec) | image#
_[Image](#image)_ | _(Optional)_
Image - docker image settings
if no specified operator uses default version from operator config | | imagePullSecrets#
_[LocalObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#localobjectreference-v1-core) array_ | _(Optional)_
ImagePullSecrets An optional list of references to secrets in the same namespace
to use for pulling images from registries
see https://kubernetes.io/docs/concepts/containers/images/#referring-to-an-imagepullsecrets-on-a-pod | | initContainers#
_[Container](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#container-v1-core) array_ | _(Optional)_
InitContainers allows adding initContainers to the pod definition.
Any errors during the execution of an initContainer will lead to a restart of the Pod.
More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ | +| livenessProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
LivenessProbe that will be added to CR pod | | logFormat#
_string_ | _(Optional)_
LogFormat for VLSelect to be configured with.
default or json | | logLevel#
_string_ | _(Optional)_
LogLevel for VLSelect to be configured with. | | minReadySeconds#
_integer_ | _(Optional)_
MinReadySeconds defines a minimum number of seconds to wait before starting update next pod
if previous in healthy state
Has no effect for VLogs and VMSingle | @@ -328,6 +332,7 @@ Appears in: [VLClusterSpec](#vlclusterspec) | port#
_string_ | _(Optional)_
Port listen address | | priorityClassName#
_string_ | _(Optional)_
PriorityClassName class assigned to the Pods | | readinessGates#
_[PodReadinessGate](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#podreadinessgate-v1-core) array_ | _(Required)_
ReadinessGates defines pod readiness gates | +| readinessProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
ReadinessProbe that will be added to CR pod | | replicaCount#
_integer_ | _(Optional)_
ReplicaCount is the expected size of the Application. | | resources#
_[ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#resourcerequirements-v1-core)_ | _(Optional)_
Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
if not defined default resources from operator config will be used | | revisionHistoryLimitCount#
_integer_ | _(Optional)_
The number of old ReplicaSets to retain to allow rollback in deployment or
maximum number of revisions that will be maintained in the Deployment revision history.
Has no effect at StatefulSets
Defaults to 10. | @@ -338,6 +343,7 @@ Appears in: [VLClusterSpec](#vlclusterspec) | securityContext#
_[SecurityContext](#securitycontext)_ | _(Optional)_
SecurityContext holds pod-level security attributes and common container settings.
This defaults to the default PodSecurityContext. | | serviceScrapeSpec#
_[VMServiceScrapeSpec](#vmservicescrapespec)_ | _(Optional)_
ServiceScrapeSpec that will be added to vlselect VMServiceScrape spec | | serviceSpec#
_[AdditionalServiceSpec](#additionalservicespec)_ | _(Optional)_
ServiceSpec that will be added to vlselect service spec | +| startupProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
StartupProbe that will be added to CR pod | | syslogSpec#
_[SyslogServerSpec](#syslogserverspec)_ | _(Optional)_
SyslogSpec defines syslog listener configuration | | terminationGracePeriodSeconds#
_integer_ | _(Optional)_
TerminationGracePeriodSeconds period for container graceful termination | | tolerations#
_[Toleration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#toleration-v1-core) array_ | _(Optional)_
Tolerations If specified, the pod's tolerations. | @@ -375,6 +381,7 @@ Appears in: [VLClusterSpec](#vlclusterspec) | image#
_[Image](#image)_ | _(Optional)_
Image - docker image settings
if no specified operator uses default version from operator config | | imagePullSecrets#
_[LocalObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#localobjectreference-v1-core) array_ | _(Optional)_
ImagePullSecrets An optional list of references to secrets in the same namespace
to use for pulling images from registries
see https://kubernetes.io/docs/concepts/containers/images/#referring-to-an-imagepullsecrets-on-a-pod | | initContainers#
_[Container](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#container-v1-core) array_ | _(Optional)_
InitContainers allows adding initContainers to the pod definition.
Any errors during the execution of an initContainer will lead to a restart of the Pod.
More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ | +| livenessProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
LivenessProbe that will be added to CR pod | | logFormat#
_string_ | _(Optional)_
LogFormat for VLSelect to be configured with.
default or json | | logLevel#
_string_ | _(Optional)_
LogLevel for VLSelect to be configured with. | | minReadySeconds#
_integer_ | _(Optional)_
MinReadySeconds defines a minimum number of seconds to wait before starting update next pod
if previous in healthy state
Has no effect for VLogs and VMSingle | @@ -385,6 +392,7 @@ Appears in: [VLClusterSpec](#vlclusterspec) | port#
_string_ | _(Optional)_
Port listen address | | priorityClassName#
_string_ | _(Optional)_
PriorityClassName class assigned to the Pods | | readinessGates#
_[PodReadinessGate](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#podreadinessgate-v1-core) array_ | _(Required)_
ReadinessGates defines pod readiness gates | +| readinessProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
ReadinessProbe that will be added to CR pod | | replicaCount#
_integer_ | _(Optional)_
ReplicaCount is the expected size of the Application. | | resources#
_[ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#resourcerequirements-v1-core)_ | _(Optional)_
Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
if not defined default resources from operator config will be used | | revisionHistoryLimitCount#
_integer_ | _(Optional)_
The number of old ReplicaSets to retain to allow rollback in deployment or
maximum number of revisions that will be maintained in the Deployment revision history.
Has no effect at StatefulSets
Defaults to 10. | @@ -395,6 +403,7 @@ Appears in: [VLClusterSpec](#vlclusterspec) | securityContext#
_[SecurityContext](#securitycontext)_ | _(Optional)_
SecurityContext holds pod-level security attributes and common container settings.
This defaults to the default PodSecurityContext. | | serviceScrapeSpec#
_[VMServiceScrapeSpec](#vmservicescrapespec)_ | _(Optional)_
ServiceScrapeSpec that will be added to vlselect VMServiceScrape spec | | serviceSpec#
_[AdditionalServiceSpec](#additionalservicespec)_ | _(Optional)_
ServiceSpec that will be added to vlselect service spec | +| startupProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
StartupProbe that will be added to CR pod | | terminationGracePeriodSeconds#
_integer_ | _(Optional)_
TerminationGracePeriodSeconds period for container graceful termination | | tolerations#
_[Toleration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#toleration-v1-core) array_ | _(Optional)_
Tolerations If specified, the pod's tolerations. | | topologySpreadConstraints#
_[TopologySpreadConstraint](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#topologyspreadconstraint-v1-core) array_ | _(Optional)_
TopologySpreadConstraints embedded kubernetes pod configuration option,
controls how pods are spread across your cluster among failure-domains
such as regions, zones, nodes, and other user-defined topology domains
https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/ | @@ -443,6 +452,7 @@ Appears in: [VLSingle](#vlsingle) | imagePullSecrets#
_[LocalObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#localobjectreference-v1-core) array_ | _(Optional)_
ImagePullSecrets An optional list of references to secrets in the same namespace
to use for pulling images from registries
see https://kubernetes.io/docs/concepts/containers/images/#referring-to-an-imagepullsecrets-on-a-pod | | initContainers#
_[Container](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#container-v1-core) array_ | _(Optional)_
InitContainers allows adding initContainers to the pod definition.
Any errors during the execution of an initContainer will lead to a restart of the Pod.
More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ | | license#
_[License](#license)_ | _(Optional)_
License allows to configure license key to be used for enterprise features.
See [here](https://docs.victoriametrics.com/victoriametrics/enterprise/#victorialogs-enterprise-features) | +| livenessProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
LivenessProbe that will be added to CR pod | | logFormat#
_string_ | _(Optional)_
LogFormat for VLSingle to be configured with. | | logIngestedRows#
_boolean_ | _(Required)_
Whether to log all the ingested log entries; this can be useful for debugging of data ingestion; see https://docs.victoriametrics.com/victorialogs/data-ingestion/ | | logLevel#
_string_ | _(Optional)_
LogLevel for VictoriaLogs to be configured with. | @@ -455,6 +465,7 @@ Appears in: [VLSingle](#vlsingle) | port#
_string_ | _(Optional)_
Port listen address | | priorityClassName#
_string_ | _(Optional)_
PriorityClassName class assigned to the Pods | | readinessGates#
_[PodReadinessGate](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#podreadinessgate-v1-core) array_ | _(Required)_
ReadinessGates defines pod readiness gates | +| readinessProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
ReadinessProbe that will be added to CR pod | | replicaCount#
_integer_ | _(Optional)_
ReplicaCount is the expected size of the Application. | | resources#
_[ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#resourcerequirements-v1-core)_ | _(Optional)_
Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
if not defined default resources from operator config will be used | | retentionMaxDiskSpaceUsageBytes#
_[BytesString](#bytesstring)_ | _(Optional)_
RetentionMaxDiskSpaceUsageBytes for the stored logs
VictoriaLogs keeps at least two last days of data in order to guarantee that the logs for the last day can be returned in queries.
This means that the total disk space usage may exceed the -retention.maxDiskSpaceUsageBytes,
if the size of the last two days of data exceeds the -retention.maxDiskSpaceUsageBytes.
https://docs.victoriametrics.com/victorialogs/#retention-by-disk-space-usage | @@ -467,6 +478,7 @@ Appears in: [VLSingle](#vlsingle) | serviceAccountName#
_string_ | _(Optional)_
ServiceAccountName is the name of the ServiceAccount to use to run the pods | | serviceScrapeSpec#
_[VMServiceScrapeSpec](#vmservicescrapespec)_ | _(Optional)_
ServiceScrapeSpec that will be added to vlsingle VMServiceScrape spec | | serviceSpec#
_[AdditionalServiceSpec](#additionalservicespec)_ | _(Optional)_
ServiceSpec that will be added to vlsingle service spec | +| startupProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
StartupProbe that will be added to CR pod | | storage#
_[PersistentVolumeClaimSpec](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#persistentvolumeclaimspec-v1-core)_ | _(Optional)_
Storage is the definition of how storage will be used by the VLSingle
by default it`s empty dir | | storageDataPath#
_string_ | _(Optional)_
StorageDataPath disables spec.storage option and overrides arg for victoria-logs binary --storageDataPath,
its users responsibility to mount proper device into given path. | | storageMetadata#
_[EmbeddedObjectMetadata](#embeddedobjectmetadata)_ | _(Optional)_
StorageMeta defines annotations and labels attached to PVC for given vlsingle CR | @@ -506,6 +518,7 @@ Appears in: [VLClusterSpec](#vlclusterspec) | image#
_[Image](#image)_ | _(Optional)_
Image - docker image settings
if no specified operator uses default version from operator config | | imagePullSecrets#
_[LocalObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#localobjectreference-v1-core) array_ | _(Optional)_
ImagePullSecrets An optional list of references to secrets in the same namespace
to use for pulling images from registries
see https://kubernetes.io/docs/concepts/containers/images/#referring-to-an-imagepullsecrets-on-a-pod | | initContainers#
_[Container](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#container-v1-core) array_ | _(Optional)_
InitContainers allows adding initContainers to the pod definition.
Any errors during the execution of an initContainer will lead to a restart of the Pod.
More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ | +| livenessProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
LivenessProbe that will be added to CR pod | | logFormat#
_string_ | _(Optional)_
LogFormat for VLStorage to be configured with.
default or json | | logIngestedRows#
_boolean_ | _(Required)_
Whether to log all the ingested log entries; this can be useful for debugging of data ingestion; see https://docs.victoriametrics.com/victorialogs/data-ingestion/ | | logLevel#
_string_ | _(Optional)_
LogLevel for VLStorage to be configured with. | @@ -521,6 +534,7 @@ Appears in: [VLClusterSpec](#vlclusterspec) | port#
_string_ | _(Optional)_
Port listen address | | priorityClassName#
_string_ | _(Optional)_
PriorityClassName class assigned to the Pods | | readinessGates#
_[PodReadinessGate](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#podreadinessgate-v1-core) array_ | _(Required)_
ReadinessGates defines pod readiness gates | +| readinessProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
ReadinessProbe that will be added to CR pod | | replicaCount#
_integer_ | _(Optional)_
ReplicaCount is the expected size of the Application. | | resources#
_[ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#resourcerequirements-v1-core)_ | _(Optional)_
Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
if not defined default resources from operator config will be used | | retentionMaxDiskSpaceUsageBytes#
_[BytesString](#bytesstring)_ | _(Optional)_
RetentionMaxDiskSpaceUsageBytes for the stored logs
VictoriaLogs keeps at least two last days of data in order to guarantee that the logs for the last day can be returned in queries.
This means that the total disk space usage may exceed the -retention.maxDiskSpaceUsageBytes,
if the size of the last two days of data exceeds the -retention.maxDiskSpaceUsageBytes.
https://docs.victoriametrics.com/victorialogs/#retention-by-disk-space-usage | @@ -534,6 +548,7 @@ Appears in: [VLClusterSpec](#vlclusterspec) | securityContext#
_[SecurityContext](#securitycontext)_ | _(Optional)_
SecurityContext holds pod-level security attributes and common container settings.
This defaults to the default PodSecurityContext. | | serviceScrapeSpec#
_[VMServiceScrapeSpec](#vmservicescrapespec)_ | _(Optional)_
ServiceScrapeSpec that will be added to vlselect VMServiceScrape spec | | serviceSpec#
_[AdditionalServiceSpec](#additionalservicespec)_ | _(Optional)_
ServiceSpec that will be added to vlselect service spec | +| startupProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
StartupProbe that will be added to CR pod | | storage#
_[StorageSpec](#storagespec)_ | _(Optional)_
Storage configures persistent volume for VLStorage | | storageDataPath#
_string_ | _(Optional)_
StorageDataPath - path to storage data | | terminationGracePeriodSeconds#
_integer_ | _(Optional)_
TerminationGracePeriodSeconds period for container graceful termination | @@ -691,6 +706,7 @@ Appears in: [VMAnomaly](#vmanomaly) | imagePullSecrets#
_[LocalObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#localobjectreference-v1-core) array_ | _(Optional)_
ImagePullSecrets An optional list of references to secrets in the same namespace
to use for pulling images from registries
see https://kubernetes.io/docs/concepts/containers/images/#referring-to-an-imagepullsecrets-on-a-pod | | initContainers#
_[Container](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#container-v1-core) array_ | _(Optional)_
InitContainers allows adding initContainers to the pod definition.
Any errors during the execution of an initContainer will lead to a restart of the Pod.
More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ | | license#
_[License](#license)_ | _(Optional)_
License allows to configure license key to be used for enterprise features.
Using license key is supported starting from VictoriaMetrics v1.94.0.
See [here](https://docs.victoriametrics.com/victoriametrics/enterprise/) | +| livenessProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
LivenessProbe that will be added to CR pod | | logLevel#
_string_ | _(Optional)_
LogLevel for VMAnomaly to be configured with.
INFO, WARN, ERROR, FATAL, PANIC | | managedMetadata#
_[ManagedObjectsMetadata](#managedobjectsmetadata)_ | _(Required)_
ManagedMetadata defines metadata that will be added to the all objects
created by operator for the given CustomResource | | minReadySeconds#
_integer_ | _(Optional)_
MinReadySeconds defines a minimum number of seconds to wait before starting update next pod
if previous in healthy state
Has no effect for VLogs and VMSingle | @@ -704,6 +720,7 @@ Appears in: [VMAnomaly](#vmanomaly) | priorityClassName#
_string_ | _(Optional)_
PriorityClassName class assigned to the Pods | | reader#
_[VMAnomalyReadersSpec](#vmanomalyreadersspec)_ | _(Required)_
Metrics source for VMAnomaly
See https://docs.victoriametrics.com/anomaly-detection/components/reader/ | | readinessGates#
_[PodReadinessGate](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#podreadinessgate-v1-core) array_ | _(Required)_
ReadinessGates defines pod readiness gates | +| readinessProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
ReadinessProbe that will be added to CR pod | | replicaCount#
_integer_ | _(Optional)_
ReplicaCount is the expected size of the Application. | | resources#
_[ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#resourcerequirements-v1-core)_ | _(Optional)_
Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
if not defined default resources from operator config will be used | | revisionHistoryLimitCount#
_integer_ | _(Optional)_
The number of old ReplicaSets to retain to allow rollback in deployment or
maximum number of revisions that will be maintained in the Deployment revision history.
Has no effect at StatefulSets
Defaults to 10. | @@ -716,6 +733,7 @@ Appears in: [VMAnomaly](#vmanomaly) | serviceAccountName#
_string_ | _(Optional)_
ServiceAccountName is the name of the ServiceAccount to use to run the pods | | serviceScrapeSpec#
_[VMServiceScrapeSpec](#vmservicescrapespec)_ | _(Optional)_
ServiceScrapeSpec that will be added to vmanomaly VMPodScrape spec | | shardCount#
_integer_ | _(Optional)_
ShardCount - numbers of shards of VMAnomaly
in this case operator will use 1 sts per shard with
replicas count according to spec.replicas. | +| startupProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
StartupProbe that will be added to CR pod | | storage#
_[StorageSpec](#storagespec)_ | _(Optional)_
Storage configures storage for StatefulSet | | terminationGracePeriodSeconds#
_integer_ | _(Optional)_
TerminationGracePeriodSeconds period for container graceful termination | | tolerations#
_[Toleration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#toleration-v1-core) array_ | _(Optional)_
Tolerations If specified, the pod's tolerations. | @@ -811,6 +829,7 @@ Appears in: [VTClusterSpec](#vtclusterspec) | image#
_[Image](#image)_ | _(Optional)_
Image - docker image settings
if no specified operator uses default version from operator config | | imagePullSecrets#
_[LocalObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#localobjectreference-v1-core) array_ | _(Optional)_
ImagePullSecrets An optional list of references to secrets in the same namespace
to use for pulling images from registries
see https://kubernetes.io/docs/concepts/containers/images/#referring-to-an-imagepullsecrets-on-a-pod | | initContainers#
_[Container](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#container-v1-core) array_ | _(Optional)_
InitContainers allows adding initContainers to the pod definition.
Any errors during the execution of an initContainer will lead to a restart of the Pod.
More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ | +| livenessProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
LivenessProbe that will be added to CR pod | | logFormat#
_string_ | _(Optional)_
LogFormat for VTInsert to be configured with.
default or json | | logLevel#
_string_ | _(Optional)_
LogLevel for VTInsert to be configured with. | | minReadySeconds#
_integer_ | _(Optional)_
MinReadySeconds defines a minimum number of seconds to wait before starting update next pod
if previous in healthy state
Has no effect for VLogs and VMSingle | @@ -821,6 +840,7 @@ Appears in: [VTClusterSpec](#vtclusterspec) | port#
_string_ | _(Optional)_
Port listen address | | priorityClassName#
_string_ | _(Optional)_
PriorityClassName class assigned to the Pods | | readinessGates#
_[PodReadinessGate](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#podreadinessgate-v1-core) array_ | _(Required)_
ReadinessGates defines pod readiness gates | +| readinessProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
ReadinessProbe that will be added to CR pod | | replicaCount#
_integer_ | _(Optional)_
ReplicaCount is the expected size of the Application. | | resources#
_[ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#resourcerequirements-v1-core)_ | _(Optional)_
Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
if not defined default resources from operator config will be used | | revisionHistoryLimitCount#
_integer_ | _(Optional)_
The number of old ReplicaSets to retain to allow rollback in deployment or
maximum number of revisions that will be maintained in the Deployment revision history.
Has no effect at StatefulSets
Defaults to 10. | @@ -831,6 +851,7 @@ Appears in: [VTClusterSpec](#vtclusterspec) | securityContext#
_[SecurityContext](#securitycontext)_ | _(Optional)_
SecurityContext holds pod-level security attributes and common container settings.
This defaults to the default PodSecurityContext. | | serviceScrapeSpec#
_[VMServiceScrapeSpec](#vmservicescrapespec)_ | _(Optional)_
ServiceScrapeSpec that will be added to vtinsert VMServiceScrape spec | | serviceSpec#
_[AdditionalServiceSpec](#additionalservicespec)_ | _(Optional)_
ServiceSpec that will be added to vtinsert service spec | +| startupProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
StartupProbe that will be added to CR pod | | terminationGracePeriodSeconds#
_integer_ | _(Optional)_
TerminationGracePeriodSeconds period for container graceful termination | | tolerations#
_[Toleration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#toleration-v1-core) array_ | _(Optional)_
Tolerations If specified, the pod's tolerations. | | topologySpreadConstraints#
_[TopologySpreadConstraint](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#topologyspreadconstraint-v1-core) array_ | _(Optional)_
TopologySpreadConstraints embedded kubernetes pod configuration option,
controls how pods are spread across your cluster among failure-domains
such as regions, zones, nodes, and other user-defined topology domains
https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/ | @@ -867,6 +888,7 @@ Appears in: [VTClusterSpec](#vtclusterspec) | image#
_[Image](#image)_ | _(Optional)_
Image - docker image settings
if no specified operator uses default version from operator config | | imagePullSecrets#
_[LocalObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#localobjectreference-v1-core) array_ | _(Optional)_
ImagePullSecrets An optional list of references to secrets in the same namespace
to use for pulling images from registries
see https://kubernetes.io/docs/concepts/containers/images/#referring-to-an-imagepullsecrets-on-a-pod | | initContainers#
_[Container](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#container-v1-core) array_ | _(Optional)_
InitContainers allows adding initContainers to the pod definition.
Any errors during the execution of an initContainer will lead to a restart of the Pod.
More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ | +| livenessProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
LivenessProbe that will be added to CR pod | | logFormat#
_string_ | _(Optional)_
LogFormat for VTSelect to be configured with.
default or json | | logLevel#
_string_ | _(Optional)_
LogLevel for VTSelect to be configured with. | | minReadySeconds#
_integer_ | _(Optional)_
MinReadySeconds defines a minimum number of seconds to wait before starting update next pod
if previous in healthy state
Has no effect for VLogs and VMSingle | @@ -877,6 +899,7 @@ Appears in: [VTClusterSpec](#vtclusterspec) | port#
_string_ | _(Optional)_
Port listen address | | priorityClassName#
_string_ | _(Optional)_
PriorityClassName class assigned to the Pods | | readinessGates#
_[PodReadinessGate](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#podreadinessgate-v1-core) array_ | _(Required)_
ReadinessGates defines pod readiness gates | +| readinessProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
ReadinessProbe that will be added to CR pod | | replicaCount#
_integer_ | _(Optional)_
ReplicaCount is the expected size of the Application. | | resources#
_[ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#resourcerequirements-v1-core)_ | _(Optional)_
Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
if not defined default resources from operator config will be used | | revisionHistoryLimitCount#
_integer_ | _(Optional)_
The number of old ReplicaSets to retain to allow rollback in deployment or
maximum number of revisions that will be maintained in the Deployment revision history.
Has no effect at StatefulSets
Defaults to 10. | @@ -887,6 +910,7 @@ Appears in: [VTClusterSpec](#vtclusterspec) | securityContext#
_[SecurityContext](#securitycontext)_ | _(Optional)_
SecurityContext holds pod-level security attributes and common container settings.
This defaults to the default PodSecurityContext. | | serviceScrapeSpec#
_[VMServiceScrapeSpec](#vmservicescrapespec)_ | _(Optional)_
ServiceScrapeSpec that will be added to vtselect VMServiceScrape spec | | serviceSpec#
_[AdditionalServiceSpec](#additionalservicespec)_ | _(Optional)_
ServiceSpec that will be added to vtselect service spec | +| startupProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
StartupProbe that will be added to CR pod | | terminationGracePeriodSeconds#
_integer_ | _(Optional)_
TerminationGracePeriodSeconds period for container graceful termination | | tolerations#
_[Toleration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#toleration-v1-core) array_ | _(Optional)_
Tolerations If specified, the pod's tolerations. | | topologySpreadConstraints#
_[TopologySpreadConstraint](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#topologyspreadconstraint-v1-core) array_ | _(Optional)_
TopologySpreadConstraints embedded kubernetes pod configuration option,
controls how pods are spread across your cluster among failure-domains
such as regions, zones, nodes, and other user-defined topology domains
https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/ | @@ -934,6 +958,7 @@ Appears in: [VTSingle](#vtsingle) | image#
_[Image](#image)_ | _(Optional)_
Image - docker image settings
if no specified operator uses default version from operator config | | imagePullSecrets#
_[LocalObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#localobjectreference-v1-core) array_ | _(Optional)_
ImagePullSecrets An optional list of references to secrets in the same namespace
to use for pulling images from registries
see https://kubernetes.io/docs/concepts/containers/images/#referring-to-an-imagepullsecrets-on-a-pod | | initContainers#
_[Container](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#container-v1-core) array_ | _(Optional)_
InitContainers allows adding initContainers to the pod definition.
Any errors during the execution of an initContainer will lead to a restart of the Pod.
More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ | +| livenessProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
LivenessProbe that will be added to CR pod | | logFormat#
_string_ | _(Optional)_
LogFormat for VTSingle to be configured with. | | logIngestedRows#
_boolean_ | _(Required)_
Whether to log all the ingested log entries; this can be useful for debugging of data ingestion;
see https://docs.victoriametrics.com/victoriatraces/#configure-and-run-victoriatraces | | logLevel#
_string_ | _(Optional)_
LogLevel for VictoriaTraces to be configured with. | @@ -946,6 +971,7 @@ Appears in: [VTSingle](#vtsingle) | port#
_string_ | _(Optional)_
Port listen address | | priorityClassName#
_string_ | _(Optional)_
PriorityClassName class assigned to the Pods | | readinessGates#
_[PodReadinessGate](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#podreadinessgate-v1-core) array_ | _(Required)_
ReadinessGates defines pod readiness gates | +| readinessProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
ReadinessProbe that will be added to CR pod | | replicaCount#
_integer_ | _(Optional)_
ReplicaCount is the expected size of the Application. | | resources#
_[ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#resourcerequirements-v1-core)_ | _(Optional)_
Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
if not defined default resources from operator config will be used | | retentionMaxDiskSpaceUsageBytes#
_[BytesString](#bytesstring)_ | _(Optional)_
RetentionMaxDiskSpaceUsageBytes for the stored traces
VictoriaTraces keeps at least two last days of data in order to guarantee that the traces for the last day can be returned in queries.
This means that the total disk space usage may exceed the -retention.maxDiskSpaceUsageBytes,
if the size of the last two days of data exceeds the -retention.maxDiskSpaceUsageBytes.
https://docs.victoriametrics.com/victoriatraces/#configure-and-run-victoriatraces | @@ -958,6 +984,7 @@ Appears in: [VTSingle](#vtsingle) | serviceAccountName#
_string_ | _(Optional)_
ServiceAccountName is the name of the ServiceAccount to use to run the pods | | serviceScrapeSpec#
_[VMServiceScrapeSpec](#vmservicescrapespec)_ | _(Optional)_
ServiceScrapeSpec that will be added to vtsingle VMServiceScrape spec | | serviceSpec#
_[AdditionalServiceSpec](#additionalservicespec)_ | _(Optional)_
ServiceSpec that will be added to vtsingle service spec | +| startupProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
StartupProbe that will be added to CR pod | | storage#
_[PersistentVolumeClaimSpec](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#persistentvolumeclaimspec-v1-core)_ | _(Optional)_
Storage is the definition of how storage will be used by the VTSingle
by default it`s empty dir | | storageDataPath#
_string_ | _(Optional)_
StorageDataPath disables spec.storage option and overrides arg for victoria-traces binary --storageDataPath,
its users responsibility to mount proper device into given path. | | storageMetadata#
_[EmbeddedObjectMetadata](#embeddedobjectmetadata)_ | _(Optional)_
StorageMeta defines annotations and labels attached to PVC for given vtsingle CR | @@ -996,6 +1023,7 @@ Appears in: [VTClusterSpec](#vtclusterspec) | image#
_[Image](#image)_ | _(Optional)_
Image - docker image settings
if no specified operator uses default version from operator config | | imagePullSecrets#
_[LocalObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#localobjectreference-v1-core) array_ | _(Optional)_
ImagePullSecrets An optional list of references to secrets in the same namespace
to use for pulling images from registries
see https://kubernetes.io/docs/concepts/containers/images/#referring-to-an-imagepullsecrets-on-a-pod | | initContainers#
_[Container](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#container-v1-core) array_ | _(Optional)_
InitContainers allows adding initContainers to the pod definition.
Any errors during the execution of an initContainer will lead to a restart of the Pod.
More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ | +| livenessProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
LivenessProbe that will be added to CR pod | | logFormat#
_string_ | _(Optional)_
LogFormat for VTStorage to be configured with.
default or json | | logIngestedRows#
_boolean_ | _(Required)_
Whether to log all the ingested log entries; this can be useful for debugging of data ingestion
see https://docs.victoriametrics.com/victoriatraces/#configure-and-run-victoriatraces | | logLevel#
_string_ | _(Optional)_
LogLevel for VTStorage to be configured with. | @@ -1011,6 +1039,7 @@ Appears in: [VTClusterSpec](#vtclusterspec) | port#
_string_ | _(Optional)_
Port listen address | | priorityClassName#
_string_ | _(Optional)_
PriorityClassName class assigned to the Pods | | readinessGates#
_[PodReadinessGate](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#podreadinessgate-v1-core) array_ | _(Required)_
ReadinessGates defines pod readiness gates | +| readinessProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
ReadinessProbe that will be added to CR pod | | replicaCount#
_integer_ | _(Optional)_
ReplicaCount is the expected size of the Application. | | resources#
_[ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#resourcerequirements-v1-core)_ | _(Optional)_
Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
if not defined default resources from operator config will be used | | retentionMaxDiskSpaceUsageBytes#
_[BytesString](#bytesstring)_ | _(Optional)_
RetentionMaxDiskSpaceUsageBytes for the stored traces
VictoriaTraces keeps at least two last days of data in order to guarantee that the traces for the last day can be returned in queries.
This means that the total disk space usage may exceed the -retention.maxDiskSpaceUsageBytes,
if the size of the last two days of data exceeds the -retention.maxDiskSpaceUsageBytes.
https://docs.victoriametrics.com/victoriatraces/#configure-and-run-victoriatraces | @@ -1024,6 +1053,7 @@ Appears in: [VTClusterSpec](#vtclusterspec) | securityContext#
_[SecurityContext](#securitycontext)_ | _(Optional)_
SecurityContext holds pod-level security attributes and common container settings.
This defaults to the default PodSecurityContext. | | serviceScrapeSpec#
_[VMServiceScrapeSpec](#vmservicescrapespec)_ | _(Optional)_
ServiceScrapeSpec that will be added to vtstorage VMServiceScrape spec | | serviceSpec#
_[AdditionalServiceSpec](#additionalservicespec)_ | _(Optional)_
ServiceSpec that will be added to vtstorage service spec | +| startupProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
StartupProbe that will be added to CR pod | | storage#
_[StorageSpec](#storagespec)_ | _(Optional)_
Storage configures persistent volume for VTStorage | | storageDataPath#
_string_ | _(Optional)_
StorageDataPath - path to storage data | | terminationGracePeriodSeconds#
_integer_ | _(Optional)_
TerminationGracePeriodSeconds period for container graceful termination | @@ -1141,6 +1171,7 @@ Appears in: [VMDistributedZoneAgent](#vmdistributedzoneagent) | imagePullSecrets#
_[LocalObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#localobjectreference-v1-core) array_ | _(Optional)_
ImagePullSecrets An optional list of references to secrets in the same namespace
to use for pulling images from registries
see https://kubernetes.io/docs/concepts/containers/images/#referring-to-an-imagepullsecrets-on-a-pod | | initContainers#
_[Container](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#container-v1-core) array_ | _(Optional)_
InitContainers allows adding initContainers to the pod definition.
Any errors during the execution of an initContainer will lead to a restart of the Pod.
More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ | | license#
_[License](#license)_ | _(Optional)_
License allows to configure license key to be used for enterprise features.
Using license key is supported starting from VictoriaMetrics v1.94.0.
See [here](https://docs.victoriametrics.com/victoriametrics/enterprise/) | +| livenessProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
LivenessProbe that will be added to CR pod | | logFormat#
_string_ | _(Optional)_
LogFormat for VMAgent to be configured with. | | logLevel#
_string_ | _(Optional)_
LogLevel for VMAgent to be configured with.
INFO, WARN, ERROR, FATAL, PANIC | | managedMetadata#
_[ManagedObjectsMetadata](#managedobjectsmetadata)_ | _(Required)_
ManagedMetadata defines metadata that will be added to the all objects
created by operator for the given CustomResource | @@ -1153,6 +1184,7 @@ Appears in: [VMDistributedZoneAgent](#vmdistributedzoneagent) | port#
_string_ | _(Optional)_
Port listen address | | priorityClassName#
_string_ | _(Optional)_
PriorityClassName class assigned to the Pods | | readinessGates#
_[PodReadinessGate](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#podreadinessgate-v1-core) array_ | _(Required)_
ReadinessGates defines pod readiness gates | +| readinessProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
ReadinessProbe that will be added to CR pod | | remoteWriteSettings#
_[VMAgentRemoteWriteSettings](#vmagentremotewritesettings)_ | _(Optional)_
RemoteWriteSettings defines global settings for all remoteWrite urls. | | replicaCount#
_integer_ | _(Optional)_
ReplicaCount is the expected size of the Application. | | resources#
_[ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#resourcerequirements-v1-core)_ | _(Optional)_
Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
if not defined default resources from operator config will be used | @@ -1163,6 +1195,7 @@ Appears in: [VMDistributedZoneAgent](#vmdistributedzoneagent) | secrets#
_string array_ | _(Optional)_
Secrets is a list of Secrets in the same namespace as the Application
object, which shall be mounted into the Application container
at /etc/vm/secrets/SECRET_NAME folder | | securityContext#
_[SecurityContext](#securitycontext)_ | _(Optional)_
SecurityContext holds pod-level security attributes and common container settings.
This defaults to the default PodSecurityContext. | | serviceAccountName#
_string_ | _(Optional)_
ServiceAccountName is the name of the ServiceAccount to use to run the pods | +| startupProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
StartupProbe that will be added to CR pod | | statefulMode#
_boolean_ | _(Optional)_
StatefulMode enables StatefulSet for `VMAgent` instead of Deployment
it allows using persistent storage for vmagent's persistentQueue | | statefulRollingUpdateStrategy#
_[StatefulSetUpdateStrategyType](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#statefulsetupdatestrategytype-v1-apps)_ | _(Optional)_
StatefulRollingUpdateStrategy allows configuration for strategyType
set it to RollingUpdate for disabling operator statefulSet rollingUpdate | | statefulStorage#
_[StorageSpec](#storagespec)_ | _(Optional)_
StatefulStorage configures storage for StatefulSet | @@ -1403,45 +1436,54 @@ Appears in: [TLSClientConfig](#tlsclientconfig), [TLSServerConfig](#tlsservercon | key_file#
_string_ | _(Optional)_
KeyFile defines path to the pre-mounted file with certificate key
mutually exclusive with KeySecretRef | | key_secret_ref#
_[SecretKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#secretkeyselector-v1-core)_ | _(Optional)_
Key defines reference for secret with certificate key content under given key
mutually exclusive with KeyFile | -#### CommonApplicationDeploymentParams +#### CommonAppsParams -CommonApplicationDeploymentParams defines common params +CommonAppsParams defines common params for deployment and statefulset specifications Appears in: [VLAgentSpec](#vlagentspec), [VLInsert](#vlinsert), [VLSelect](#vlselect), [VLSingleSpec](#vlsinglespec), [VLStorage](#vlstorage), [VLogsSpec](#vlogsspec), [VMAgentSpec](#vmagentspec), [VMAlertSpec](#vmalertspec), [VMAlertmanagerSpec](#vmalertmanagerspec), [VMAnomalySpec](#vmanomalyspec), [VMAuthLoadBalancerSpec](#vmauthloadbalancerspec), [VMAuthSpec](#vmauthspec), [VMDistributedZoneAgentSpec](#vmdistributedzoneagentspec), [VMInsert](#vminsert), [VMSelect](#vmselect), [VMSingleSpec](#vmsinglespec), [VMStorage](#vmstorage), [VTInsert](#vtinsert), [VTSelect](#vtselect), [VTSingleSpec](#vtsinglespec), [VTStorage](#vtstorage) | Field | Description | | --- | --- | -| affinity#
_[Affinity](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#affinity-v1-core)_ | _(Optional)_
Affinity If specified, the pod's scheduling constraints. | -| configMaps#
_string array_ | _(Optional)_
ConfigMaps is a list of ConfigMaps in the same namespace as the Application
object, which shall be mounted into the Application container
at /etc/vm/configs/CONFIGMAP_NAME folder | -| containers#
_[Container](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#container-v1-core) array_ | _(Optional)_
Containers property allows to inject additions sidecars or to patch existing containers.
It can be useful for proxies, backup, etc. | -| disableAutomountServiceAccountToken#
_boolean_ | _(Optional)_
DisableAutomountServiceAccountToken whether to disable serviceAccount auto mount by Kubernetes (available from v0.54.0).
Operator will conditionally create volumes and volumeMounts for containers if it requires k8s API access.
For example, vmagent and vm-config-reloader requires k8s API access.
Operator creates volumes with name: "kube-api-access", which can be used as volumeMount for extraContainers if needed.
And also adds VolumeMounts at /var/run/secrets/kubernetes.io/serviceaccount. | -| dnsConfig#
_[PodDNSConfig](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#poddnsconfig-v1-core)_ | _(Optional)_
Specifies the DNS parameters of a pod.
Parameters specified here will be merged to the generated DNS
configuration based on DNSPolicy. | -| dnsPolicy#
_[DNSPolicy](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#dnspolicy-v1-core)_ | _(Optional)_
DNSPolicy sets DNS policy for the pod | -| extraArgs#
_object (keys:string, values:string)_ | _(Optional)_
ExtraArgs that will be passed to the application container
for example remoteWrite.tmpDataPath: /tmp | -| extraEnvs#
_[EnvVar](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#envvar-v1-core) array_ | _(Optional)_
ExtraEnvs that will be passed to the application container | -| extraEnvsFrom#
_[EnvFromSource](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#envfromsource-v1-core) array_ | _(Optional)_
ExtraEnvsFrom defines source of env variables for the application container
could either be secret or configmap | -| hostAliases#
_[HostAlias](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#hostalias-v1-core) array_ | _(Optional)_
HostAliases provides mapping for ip and hostname,
that would be propagated to pod,
cannot be used with HostNetwork. | -| hostNetwork#
_boolean_ | _(Optional)_
HostNetwork controls whether the pod may use the node network namespace | -| host_aliases#
_[HostAlias](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#hostalias-v1-core) array_ | _(Optional)_
HostAliasesUnderScore provides mapping for ip and hostname,
that would be propagated to pod,
cannot be used with HostNetwork.
Has Priority over hostAliases field | -| imagePullSecrets#
_[LocalObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#localobjectreference-v1-core) array_ | _(Optional)_
ImagePullSecrets An optional list of references to secrets in the same namespace
to use for pulling images from registries
see https://kubernetes.io/docs/concepts/containers/images/#referring-to-an-imagepullsecrets-on-a-pod | -| initContainers#
_[Container](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#container-v1-core) array_ | _(Optional)_
InitContainers allows adding initContainers to the pod definition.
Any errors during the execution of an initContainer will lead to a restart of the Pod.
More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ | -| minReadySeconds#
_integer_ | _(Optional)_
MinReadySeconds defines a minimum number of seconds to wait before starting update next pod
if previous in healthy state
Has no effect for VLogs and VMSingle | -| nodeSelector#
_object (keys:string, values:string)_ | _(Optional)_
NodeSelector Define which Nodes the Pods are scheduled on. | -| paused#
_boolean_ | _(Optional)_
Paused If set to true all actions on the underlying managed objects are not
going to be performed, except for delete actions. | -| priorityClassName#
_string_ | _(Optional)_
PriorityClassName class assigned to the Pods | -| readinessGates#
_[PodReadinessGate](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#podreadinessgate-v1-core) array_ | _(Required)_
ReadinessGates defines pod readiness gates | -| replicaCount#
_integer_ | _(Optional)_
ReplicaCount is the expected size of the Application. | -| revisionHistoryLimitCount#
_integer_ | _(Optional)_
The number of old ReplicaSets to retain to allow rollback in deployment or
maximum number of revisions that will be maintained in the Deployment revision history.
Has no effect at StatefulSets
Defaults to 10. | -| runtimeClassName#
_string_ | _(Optional)_
RuntimeClassName - defines runtime class for kubernetes pod.
https://kubernetes.io/docs/concepts/containers/runtime-class/ | -| schedulerName#
_string_ | _(Optional)_
SchedulerName - defines kubernetes scheduler name | -| secrets#
_string array_ | _(Optional)_
Secrets is a list of Secrets in the same namespace as the Application
object, which shall be mounted into the Application container
at /etc/vm/secrets/SECRET_NAME folder | -| securityContext#
_[SecurityContext](#securitycontext)_ | _(Optional)_
SecurityContext holds pod-level security attributes and common container settings.
This defaults to the default PodSecurityContext. | -| terminationGracePeriodSeconds#
_integer_ | _(Optional)_
TerminationGracePeriodSeconds period for container graceful termination | -| tolerations#
_[Toleration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#toleration-v1-core) array_ | _(Optional)_
Tolerations If specified, the pod's tolerations. | -| topologySpreadConstraints#
_[TopologySpreadConstraint](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#topologyspreadconstraint-v1-core) array_ | _(Optional)_
TopologySpreadConstraints embedded kubernetes pod configuration option,
controls how pods are spread across your cluster among failure-domains
such as regions, zones, nodes, and other user-defined topology domains
https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/ | -| volumeMounts#
_[VolumeMount](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#volumemount-v1-core) array_ | _(Optional)_
VolumeMounts allows configuration of additional VolumeMounts on the output Deployment/StatefulSet definition.
VolumeMounts specified will be appended to other VolumeMounts in the Application container | -| volumes#
_[Volume](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#volume-v1-core) array_ | _(Required)_
Volumes allows configuration of additional volumes on the output Deployment/StatefulSet definition.
Volumes specified will be appended to other volumes that are generated.
/ +optional | +| affinity#
_[Affinity](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#affinity-v1-core)_ | _(Optional)_
Affinity If specified, the pod's scheduling constraints. | +| configMaps#
_string array_ | _(Optional)_
ConfigMaps is a list of ConfigMaps in the same namespace as the Application
object, which shall be mounted into the Application container
at /etc/vm/configs/CONFIGMAP_NAME folder | +| containers#
_[Container](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#container-v1-core) array_ | _(Optional)_
Containers property allows to inject additions sidecars or to patch existing containers.
It can be useful for proxies, backup, etc. | +| disableAutomountServiceAccountToken#
_boolean_ | _(Optional)_
DisableAutomountServiceAccountToken whether to disable serviceAccount auto mount by Kubernetes (available from v0.54.0).
Operator will conditionally create volumes and volumeMounts for containers if it requires k8s API access.
For example, vmagent and vm-config-reloader requires k8s API access.
Operator creates volumes with name: "kube-api-access", which can be used as volumeMount for extraContainers if needed.
And also adds VolumeMounts at /var/run/secrets/kubernetes.io/serviceaccount. | +| disableSelfServiceScrape#
_boolean_ | _(Optional)_
DisableSelfServiceScrape controls creation of VMServiceScrape by operator
for the application.
Has priority over `VM_DISABLESELFSERVICESCRAPECREATION` operator env variable | +| dnsConfig#
_[PodDNSConfig](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#poddnsconfig-v1-core)_ | _(Optional)_
Specifies the DNS parameters of a pod.
Parameters specified here will be merged to the generated DNS
configuration based on DNSPolicy. | +| dnsPolicy#
_[DNSPolicy](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#dnspolicy-v1-core)_ | _(Optional)_
DNSPolicy sets DNS policy for the pod | +| extraArgs#
_object (keys:string, values:string)_ | _(Optional)_
ExtraArgs that will be passed to the application container
for example remoteWrite.tmpDataPath: /tmp | +| extraEnvs#
_[EnvVar](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#envvar-v1-core) array_ | _(Optional)_
ExtraEnvs that will be passed to the application container | +| extraEnvsFrom#
_[EnvFromSource](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#envfromsource-v1-core) array_ | _(Optional)_
ExtraEnvsFrom defines source of env variables for the application container
could either be secret or configmap | +| hostAliases#
_[HostAlias](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#hostalias-v1-core) array_ | _(Optional)_
HostAliases provides mapping for ip and hostname,
that would be propagated to pod,
cannot be used with HostNetwork. | +| hostNetwork#
_boolean_ | _(Optional)_
HostNetwork controls whether the pod may use the node network namespace | +| host_aliases#
_[HostAlias](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#hostalias-v1-core) array_ | _(Optional)_
HostAliasesUnderScore provides mapping for ip and hostname,
that would be propagated to pod,
cannot be used with HostNetwork.
Has Priority over hostAliases field | +| image#
_[Image](#image)_ | _(Optional)_
Image - docker image settings
if no specified operator uses default version from operator config | +| imagePullSecrets#
_[LocalObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#localobjectreference-v1-core) array_ | _(Optional)_
ImagePullSecrets An optional list of references to secrets in the same namespace
to use for pulling images from registries
see https://kubernetes.io/docs/concepts/containers/images/#referring-to-an-imagepullsecrets-on-a-pod | +| initContainers#
_[Container](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#container-v1-core) array_ | _(Optional)_
InitContainers allows adding initContainers to the pod definition.
Any errors during the execution of an initContainer will lead to a restart of the Pod.
More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ | +| livenessProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
LivenessProbe that will be added to CR pod | +| minReadySeconds#
_integer_ | _(Optional)_
MinReadySeconds defines a minimum number of seconds to wait before starting update next pod
if previous in healthy state
Has no effect for VLogs and VMSingle | +| nodeSelector#
_object (keys:string, values:string)_ | _(Optional)_
NodeSelector Define which Nodes the Pods are scheduled on. | +| paused#
_boolean_ | _(Optional)_
Paused If set to true all actions on the underlying managed objects are not
going to be performed, except for delete actions. | +| port#
_string_ | _(Optional)_
Port listen address | +| priorityClassName#
_string_ | _(Optional)_
PriorityClassName class assigned to the Pods | +| readinessGates#
_[PodReadinessGate](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#podreadinessgate-v1-core) array_ | _(Required)_
ReadinessGates defines pod readiness gates | +| readinessProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
ReadinessProbe that will be added to CR pod | +| replicaCount#
_integer_ | _(Optional)_
ReplicaCount is the expected size of the Application. | +| resources#
_[ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#resourcerequirements-v1-core)_ | _(Optional)_
Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
if not defined default resources from operator config will be used | +| revisionHistoryLimitCount#
_integer_ | _(Optional)_
The number of old ReplicaSets to retain to allow rollback in deployment or
maximum number of revisions that will be maintained in the Deployment revision history.
Has no effect at StatefulSets
Defaults to 10. | +| runtimeClassName#
_string_ | _(Optional)_
RuntimeClassName - defines runtime class for kubernetes pod.
https://kubernetes.io/docs/concepts/containers/runtime-class/ | +| schedulerName#
_string_ | _(Optional)_
SchedulerName - defines kubernetes scheduler name | +| secrets#
_string array_ | _(Optional)_
Secrets is a list of Secrets in the same namespace as the Application
object, which shall be mounted into the Application container
at /etc/vm/secrets/SECRET_NAME folder | +| securityContext#
_[SecurityContext](#securitycontext)_ | _(Optional)_
SecurityContext holds pod-level security attributes and common container settings.
This defaults to the default PodSecurityContext. | +| startupProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
StartupProbe that will be added to CR pod | +| terminationGracePeriodSeconds#
_integer_ | _(Optional)_
TerminationGracePeriodSeconds period for container graceful termination | +| tolerations#
_[Toleration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#toleration-v1-core) array_ | _(Optional)_
Tolerations If specified, the pod's tolerations. | +| topologySpreadConstraints#
_[TopologySpreadConstraint](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#topologyspreadconstraint-v1-core) array_ | _(Optional)_
TopologySpreadConstraints embedded kubernetes pod configuration option,
controls how pods are spread across your cluster among failure-domains
such as regions, zones, nodes, and other user-defined topology domains
https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/ | +| useDefaultResources#
_boolean_ | _(Optional)_
UseDefaultResources controls resource settings
By default, operator sets built-in resource requirements | +| useStrictSecurity#
_boolean_ | _(Optional)_
UseStrictSecurity enables strict security mode for component
it restricts disk writes access
uses non-root user out of the box
drops not needed security permissions | +| volumeMounts#
_[VolumeMount](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#volumemount-v1-core) array_ | _(Optional)_
VolumeMounts allows configuration of additional VolumeMounts on the output Deployment/StatefulSet definition.
VolumeMounts specified will be appended to other VolumeMounts in the Application container | +| volumes#
_[Volume](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#volume-v1-core) array_ | _(Required)_
Volumes allows configuration of additional volumes on the output Deployment/StatefulSet definition.
Volumes specified will be appended to other volumes that are generated.
/ +optional | #### CommonConfigReloaderParams @@ -1456,22 +1498,6 @@ Appears in: [VMAgentSpec](#vmagentspec), [VMAlertSpec](#vmalertspec), [VMAlertma | configReloaderResources#
_[ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#resourcerequirements-v1-core)_ | _(Optional)_
ConfigReloaderResources config-reloader container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
if not defined default resources from operator config will be used | | useVMConfigReloader#
_boolean_ | _(Optional)_
UseVMConfigReloader replaces prometheus-like config-reloader
with vm one. It uses secrets watch instead of file watch
which greatly increases speed of config updates
Removed since v0.67.0: this property is ignored and no longer needed | -#### CommonDefaultableParams - -CommonDefaultableParams contains Application settings -with known values populated from operator configuration - -Appears in: [VLAgentSpec](#vlagentspec), [VLInsert](#vlinsert), [VLSelect](#vlselect), [VLSingleSpec](#vlsinglespec), [VLStorage](#vlstorage), [VLogsSpec](#vlogsspec), [VMAgentSpec](#vmagentspec), [VMAlertSpec](#vmalertspec), [VMAlertmanagerSpec](#vmalertmanagerspec), [VMAnomalySpec](#vmanomalyspec), [VMAuthLoadBalancerSpec](#vmauthloadbalancerspec), [VMAuthSpec](#vmauthspec), [VMDistributedZoneAgentSpec](#vmdistributedzoneagentspec), [VMInsert](#vminsert), [VMSelect](#vmselect), [VMSingleSpec](#vmsinglespec), [VMStorage](#vmstorage), [VTInsert](#vtinsert), [VTSelect](#vtselect), [VTSingleSpec](#vtsinglespec), [VTStorage](#vtstorage) - -| Field | Description | -| --- | --- | -| disableSelfServiceScrape#
_boolean_ | _(Optional)_
DisableSelfServiceScrape controls creation of VMServiceScrape by operator
for the application.
Has priority over `VM_DISABLESELFSERVICESCRAPECREATION` operator env variable | -| image#
_[Image](#image)_ | _(Optional)_
Image - docker image settings
if no specified operator uses default version from operator config | -| port#
_string_ | _(Optional)_
Port listen address | -| resources#
_[ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#resourcerequirements-v1-core)_ | _(Optional)_
Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
if not defined default resources from operator config will be used | -| useDefaultResources#
_boolean_ | _(Optional)_
UseDefaultResources controls resource settings
By default, operator sets built-in resource requirements | -| useStrictSecurity#
_boolean_ | _(Optional)_
UseStrictSecurity enables strict security mode for component
it restricts disk writes access
uses non-root user out of the box
drops not needed security permissions | - #### CommonRelabelParams CommonRelabelParams defines params for relabelling @@ -1795,20 +1821,6 @@ Appears in: [VLAgentSpec](#vlagentspec), [VLInsert](#vlinsert), [VLSelect](#vlse | selectorLabels#
_object (keys:string, values:string)_ | _(Optional)_
replaces default labels selector generated by operator
it's useful when you need to create custom budget | | unhealthyPodEvictionPolicy#
_string_ | _(Optional)_
UnhealthyPodEvictionPolicy defines the criteria for when unhealthy pods
Valid policies are IfHealthyBudget and AlwaysAllow.
If no policy is specified, the default behavior will be used,
which corresponds to the IfHealthyBudget policy.
Available from operator v0.64.0 | -#### EmbeddedProbes - -EmbeddedProbes - it allows to override some probe params. -its not necessary to specify all options, -operator will replace missing spec with default values. - -Appears in: [VLAgentSpec](#vlagentspec), [VLInsert](#vlinsert), [VLSelect](#vlselect), [VLSingleSpec](#vlsinglespec), [VLStorage](#vlstorage), [VLogsSpec](#vlogsspec), [VMAgentSpec](#vmagentspec), [VMAlertSpec](#vmalertspec), [VMAlertmanagerSpec](#vmalertmanagerspec), [VMAnomalySpec](#vmanomalyspec), [VMAuthLoadBalancerSpec](#vmauthloadbalancerspec), [VMAuthSpec](#vmauthspec), [VMDistributedZoneAgentSpec](#vmdistributedzoneagentspec), [VMInsert](#vminsert), [VMSelect](#vmselect), [VMSingleSpec](#vmsinglespec), [VMStorage](#vmstorage), [VTInsert](#vtinsert), [VTSelect](#vtselect), [VTSingleSpec](#vtsinglespec), [VTStorage](#vtstorage) - -| Field | Description | -| --- | --- | -| livenessProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
LivenessProbe that will be added CRD pod | -| readinessProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
ReadinessProbe that will be added CRD pod | -| startupProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
StartupProbe that will be added to CRD pod | - #### EmbeddedVPA EmbeddedVPA embeds VerticalPodAutoscaler spec v1. @@ -1998,7 +2010,7 @@ Appears in: [VMScrapeConfigSpec](#vmscrapeconfigspec) Image defines docker image settings -Appears in: [CommonDefaultableParams](#commondefaultableparams), [VLAgentSpec](#vlagentspec), [VLInsert](#vlinsert), [VLSelect](#vlselect), [VLSingleSpec](#vlsinglespec), [VLStorage](#vlstorage), [VLogsSpec](#vlogsspec), [VMAgentSpec](#vmagentspec), [VMAlertSpec](#vmalertspec), [VMAlertmanagerSpec](#vmalertmanagerspec), [VMAnomalySpec](#vmanomalyspec), [VMAuthLoadBalancerSpec](#vmauthloadbalancerspec), [VMAuthSpec](#vmauthspec), [VMBackup](#vmbackup), [VMDistributedZoneAgentSpec](#vmdistributedzoneagentspec), [VMInsert](#vminsert), [VMSelect](#vmselect), [VMSingleSpec](#vmsinglespec), [VMStorage](#vmstorage), [VTInsert](#vtinsert), [VTSelect](#vtselect), [VTSingleSpec](#vtsinglespec), [VTStorage](#vtstorage) +Appears in: [CommonAppsParams](#commonappsparams), [VLAgentSpec](#vlagentspec), [VLInsert](#vlinsert), [VLSelect](#vlselect), [VLSingleSpec](#vlsinglespec), [VLStorage](#vlstorage), [VLogsSpec](#vlogsspec), [VMAgentSpec](#vmagentspec), [VMAlertSpec](#vmalertspec), [VMAlertmanagerSpec](#vmalertmanagerspec), [VMAnomalySpec](#vmanomalyspec), [VMAuthLoadBalancerSpec](#vmauthloadbalancerspec), [VMAuthSpec](#vmauthspec), [VMBackup](#vmbackup), [VMDistributedZoneAgentSpec](#vmdistributedzoneagentspec), [VMInsert](#vminsert), [VMSelect](#vmselect), [VMSingleSpec](#vmsinglespec), [VMStorage](#vmstorage), [VTInsert](#vtinsert), [VTSelect](#vtselect), [VTSingleSpec](#vtsinglespec), [VTStorage](#vtstorage) | Field | Description | | --- | --- | @@ -2633,7 +2645,7 @@ Appears in: [OAuth2](#oauth2), [TLSConfig](#tlsconfig) SecurityContext extends PodSecurityContext with ContainerSecurityContext It allows to globally configure security params for pod and all containers -Appears in: [CommonApplicationDeploymentParams](#commonapplicationdeploymentparams), [VLAgentSpec](#vlagentspec), [VLInsert](#vlinsert), [VLSelect](#vlselect), [VLSingleSpec](#vlsinglespec), [VLStorage](#vlstorage), [VLogsSpec](#vlogsspec), [VMAgentSpec](#vmagentspec), [VMAlertSpec](#vmalertspec), [VMAlertmanagerSpec](#vmalertmanagerspec), [VMAnomalySpec](#vmanomalyspec), [VMAuthLoadBalancerSpec](#vmauthloadbalancerspec), [VMAuthSpec](#vmauthspec), [VMDistributedZoneAgentSpec](#vmdistributedzoneagentspec), [VMInsert](#vminsert), [VMSelect](#vmselect), [VMSingleSpec](#vmsinglespec), [VMStorage](#vmstorage), [VTInsert](#vtinsert), [VTSelect](#vtselect), [VTSingleSpec](#vtsinglespec), [VTStorage](#vtstorage) +Appears in: [CommonAppsParams](#commonappsparams), [VLAgentSpec](#vlagentspec), [VLInsert](#vlinsert), [VLSelect](#vlselect), [VLSingleSpec](#vlsinglespec), [VLStorage](#vlstorage), [VLogsSpec](#vlogsspec), [VMAgentSpec](#vmagentspec), [VMAlertSpec](#vmalertspec), [VMAlertmanagerSpec](#vmalertmanagerspec), [VMAnomalySpec](#vmanomalyspec), [VMAuthLoadBalancerSpec](#vmauthloadbalancerspec), [VMAuthSpec](#vmauthspec), [VMDistributedZoneAgentSpec](#vmdistributedzoneagentspec), [VMInsert](#vminsert), [VMSelect](#vmselect), [VMSingleSpec](#vmsinglespec), [VMStorage](#vmstorage), [VTInsert](#vtinsert), [VTSelect](#vtselect), [VTSingleSpec](#vtsinglespec), [VTStorage](#vtstorage) #### Sigv4Config @@ -3088,6 +3100,7 @@ Appears in: [VLogs](#vlogs) | image#
_[Image](#image)_ | _(Optional)_
Image - docker image settings
if no specified operator uses default version from operator config | | imagePullSecrets#
_[LocalObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#localobjectreference-v1-core) array_ | _(Optional)_
ImagePullSecrets An optional list of references to secrets in the same namespace
to use for pulling images from registries
see https://kubernetes.io/docs/concepts/containers/images/#referring-to-an-imagepullsecrets-on-a-pod | | initContainers#
_[Container](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#container-v1-core) array_ | _(Optional)_
InitContainers allows adding initContainers to the pod definition.
Any errors during the execution of an initContainer will lead to a restart of the Pod.
More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ | +| livenessProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
LivenessProbe that will be added to CR pod | | logFormat#
_string_ | _(Optional)_
LogFormat for VLogs to be configured with. | | logIngestedRows#
_boolean_ | _(Required)_
Whether to log all the ingested log entries; this can be useful for debugging of data ingestion; see https://docs.victoriametrics.com/victorialogs/data-ingestion/ | | logLevel#
_string_ | _(Optional)_
LogLevel for VictoriaLogs to be configured with. | @@ -3100,6 +3113,7 @@ Appears in: [VLogs](#vlogs) | port#
_string_ | _(Optional)_
Port listen address | | priorityClassName#
_string_ | _(Optional)_
PriorityClassName class assigned to the Pods | | readinessGates#
_[PodReadinessGate](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#podreadinessgate-v1-core) array_ | _(Required)_
ReadinessGates defines pod readiness gates | +| readinessProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
ReadinessProbe that will be added to CR pod | | removePvcAfterDelete#
_boolean_ | _(Optional)_
RemovePvcAfterDelete - if true, controller adds ownership to pvc
and after VLogs object deletion - pvc will be garbage collected
by controller manager | | replicaCount#
_integer_ | _(Optional)_
ReplicaCount is the expected size of the Application. | | resources#
_[ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#resourcerequirements-v1-core)_ | _(Optional)_
Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
if not defined default resources from operator config will be used | @@ -3112,6 +3126,7 @@ Appears in: [VLogs](#vlogs) | serviceAccountName#
_string_ | _(Optional)_
ServiceAccountName is the name of the ServiceAccount to use to run the pods | | serviceScrapeSpec#
_[VMServiceScrapeSpec](#vmservicescrapespec)_ | _(Optional)_
ServiceScrapeSpec that will be added to vlogs VMServiceScrape spec | | serviceSpec#
_[AdditionalServiceSpec](#additionalservicespec)_ | _(Optional)_
ServiceSpec that will be added to vlogs service spec | +| startupProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
StartupProbe that will be added to CR pod | | storage#
_[PersistentVolumeClaimSpec](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#persistentvolumeclaimspec-v1-core)_ | _(Optional)_
Storage is the definition of how storage will be used by the VLogs
by default it`s empty dir | | storageDataPath#
_string_ | _(Optional)_
StorageDataPath disables spec.storage option and overrides arg for victoria-logs binary --storageDataPath,
its users responsibility to mount proper device into given path. | | storageMetadata#
_[EmbeddedObjectMetadata](#embeddedobjectmetadata)_ | _(Optional)_
StorageMeta defines annotations and labels attached to PVC for given vlogs CR | @@ -3221,6 +3236,7 @@ Appears in: [VMAgent](#vmagent) | inlineScrapeConfig#
_string_ | _(Optional)_
InlineScrapeConfig As scrape configs are appended, the user is responsible to make sure it
is valid. Note that using this feature may expose the possibility to
break upgrades of VMAgent. It is advised to review VMAgent release
notes to ensure that no incompatible scrape configs are going to break
VMAgent after the upgrade.
it should be defined as single yaml file.
inlineScrapeConfig: \|
- job_name: "prometheus"
static_configs:
- targets: ["localhost:9090"] | | insertPorts#
_[InsertPorts](#insertports)_ | _(Required)_
InsertPorts - additional listen ports for data ingestion. | | license#
_[License](#license)_ | _(Optional)_
License allows to configure license key to be used for enterprise features.
Using license key is supported starting from VictoriaMetrics v1.94.0.
See [here](https://docs.victoriametrics.com/victoriametrics/enterprise/) | +| livenessProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
LivenessProbe that will be added to CR pod | | logFormat#
_string_ | _(Optional)_
LogFormat for VMAgent to be configured with. | | logLevel#
_string_ | _(Optional)_
LogLevel for VMAgent to be configured with.
INFO, WARN, ERROR, FATAL, PANIC | | managedMetadata#
_[ManagedObjectsMetadata](#managedobjectsmetadata)_ | _(Required)_
ManagedMetadata defines metadata that will be added to the all objects
created by operator for the given CustomResource | @@ -3246,6 +3262,7 @@ Appears in: [VMAgent](#vmagent) | probeScrapeRelabelTemplate#
_[RelabelConfig](#relabelconfig) array_ | _(Optional)_
ProbeScrapeRelabelTemplate defines relabel config, that will be added to each VMProbeScrape.
it's useful for adding specific labels to all targets | | probeSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#labelselector-v1-meta)_ | _(Optional)_
ProbeSelector defines VMProbe to be selected for target probing.
Works in combination with NamespaceSelector.
NamespaceSelector nil - only objects at VMAgent namespace.
Selector nil - only objects at NamespaceSelector namespaces.
If both nil - behaviour controlled by selectAllByDefault | | readinessGates#
_[PodReadinessGate](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#podreadinessgate-v1-core) array_ | _(Required)_
ReadinessGates defines pod readiness gates | +| readinessProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
ReadinessProbe that will be added to CR pod | | relabelConfig#
_[ConfigMapKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#configmapkeyselector-v1-core)_ | _(Optional)_
RelabelConfig ConfigMap with global relabel config -remoteWrite.relabelConfig
This relabeling is applied to all the collected metrics before sending them to remote storage. | | remoteWrite#
_[VMAgentRemoteWriteSpec](#vmagentremotewritespec) array_ | _(Required)_
RemoteWrite list of victoria metrics /some other remote write system
for vm it must looks like: http://victoria-metrics-single:8428/api/v1/write
or for cluster different url
https://docs.victoriametrics.com/victoriametrics/vmagent/#splitting-data-streams-among-multiple-systems | | remoteWriteSettings#
_[VMAgentRemoteWriteSettings](#vmagentremotewritesettings)_ | _(Optional)_
RemoteWriteSettings defines global settings for all remoteWrite urls. | @@ -3272,6 +3289,7 @@ Appears in: [VMAgent](#vmagent) | serviceScrapeSpec#
_[VMServiceScrapeSpec](#vmservicescrapespec)_ | _(Optional)_
ServiceScrapeSpec that will be added to vmagent VMServiceScrape spec | | serviceSpec#
_[AdditionalServiceSpec](#additionalservicespec)_ | _(Optional)_
ServiceSpec that will be added to vmagent service spec | | shardCount#
_integer_ | _(Optional)_
ShardCount - numbers of shards of VMAgent
in this case operator will use 1 deployment/sts per shard with
replicas count according to spec.replicas,
see [here](https://docs.victoriametrics.com/victoriametrics/vmagent/#scraping-big-number-of-targets) | +| startupProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
StartupProbe that will be added to CR pod | | statefulMode#
_boolean_ | _(Optional)_
StatefulMode enables StatefulSet for `VMAgent` instead of Deployment
it allows using persistent storage for vmagent's persistentQueue | | statefulRollingUpdateStrategy#
_[StatefulSetUpdateStrategyType](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#statefulsetupdatestrategytype-v1-apps)_ | _(Optional)_
StatefulRollingUpdateStrategy allows configuration for strategyType
set it to RollingUpdate for disabling operator statefulSet rollingUpdate | | statefulStorage#
_[StorageSpec](#storagespec)_ | _(Optional)_
StatefulStorage configures storage for StatefulSet | @@ -3397,6 +3415,7 @@ Appears in: [VMAlert](#vmalert) | imagePullSecrets#
_[LocalObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#localobjectreference-v1-core) array_ | _(Optional)_
ImagePullSecrets An optional list of references to secrets in the same namespace
to use for pulling images from registries
see https://kubernetes.io/docs/concepts/containers/images/#referring-to-an-imagepullsecrets-on-a-pod | | initContainers#
_[Container](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#container-v1-core) array_ | _(Optional)_
InitContainers allows adding initContainers to the pod definition.
Any errors during the execution of an initContainer will lead to a restart of the Pod.
More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ | | license#
_[License](#license)_ | _(Optional)_
License allows to configure license key to be used for enterprise features.
Using license key is supported starting from VictoriaMetrics v1.94.0.
See [here](https://docs.victoriametrics.com/victoriametrics/enterprise/) | +| livenessProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
LivenessProbe that will be added to CR pod | | logFormat#
_string_ | _(Optional)_
LogFormat for VMAlert to be configured with.
default or json | | logLevel#
_string_ | _(Optional)_
LogLevel for VMAlert to be configured with. | | managedMetadata#
_[ManagedObjectsMetadata](#managedobjectsmetadata)_ | _(Required)_
ManagedMetadata defines metadata that will be added to the all objects
created by operator for the given CustomResource | @@ -3411,6 +3430,7 @@ Appears in: [VMAlert](#vmalert) | port#
_string_ | _(Optional)_
Port listen address | | priorityClassName#
_string_ | _(Optional)_
PriorityClassName class assigned to the Pods | | readinessGates#
_[PodReadinessGate](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#podreadinessgate-v1-core) array_ | _(Required)_
ReadinessGates defines pod readiness gates | +| readinessProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
ReadinessProbe that will be added to CR pod | | remoteRead#
_[VMAlertRemoteReadSpec](#vmalertremotereadspec)_ | _(Optional)_
RemoteRead Optional URL to read vmalert state (persisted via RemoteWrite)
This configuration only makes sense if alerts state has been successfully
persisted (via RemoteWrite) before.
see -remoteRead.url docs in vmalerts for details.
E.g. http://127.0.0.1:8428 | | remoteWrite#
_[VMAlertRemoteWriteSpec](#vmalertremotewritespec)_ | _(Optional)_
RemoteWrite Optional URL to remote-write compatible storage to persist
vmalert state and rule results to.
Rule results will be persisted according to each rule.
Alerts state will be persisted in the form of time series named ALERTS and ALERTS_FOR_STATE
see -remoteWrite.url docs in vmalerts for details.
E.g. http://127.0.0.1:8428 | | replicaCount#
_integer_ | _(Optional)_
ReplicaCount is the expected size of the Application. | @@ -3428,6 +3448,7 @@ Appears in: [VMAlert](#vmalert) | serviceAccountName#
_string_ | _(Optional)_
ServiceAccountName is the name of the ServiceAccount to use to run the pods | | serviceScrapeSpec#
_[VMServiceScrapeSpec](#vmservicescrapespec)_ | _(Optional)_
ServiceScrapeSpec that will be added to vmalert VMServiceScrape spec | | serviceSpec#
_[AdditionalServiceSpec](#additionalservicespec)_ | _(Optional)_
ServiceSpec that will be added to vmalert service spec | +| startupProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
StartupProbe that will be added to CR pod | | terminationGracePeriodSeconds#
_integer_ | _(Optional)_
TerminationGracePeriodSeconds period for container graceful termination | | tolerations#
_[Toleration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#toleration-v1-core) array_ | _(Optional)_
Tolerations If specified, the pod's tolerations. | | topologySpreadConstraints#
_[TopologySpreadConstraint](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#topologyspreadconstraint-v1-core) array_ | _(Optional)_
TopologySpreadConstraints embedded kubernetes pod configuration option,
controls how pods are spread across your cluster among failure-domains
such as regions, zones, nodes, and other user-defined topology domains
https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/ | @@ -3541,6 +3562,7 @@ Appears in: [VMAlertmanager](#vmalertmanager) | imagePullSecrets#
_[LocalObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#localobjectreference-v1-core) array_ | _(Optional)_
ImagePullSecrets An optional list of references to secrets in the same namespace
to use for pulling images from registries
see https://kubernetes.io/docs/concepts/containers/images/#referring-to-an-imagepullsecrets-on-a-pod | | initContainers#
_[Container](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#container-v1-core) array_ | _(Optional)_
InitContainers allows adding initContainers to the pod definition.
Any errors during the execution of an initContainer will lead to a restart of the Pod.
More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ | | listenLocal#
_boolean_ | _(Optional)_
ListenLocal makes the VMAlertmanager server listen on loopback, so that it
does not bind against the Pod IP. Note this is only for the VMAlertmanager
UI, not the gossip communication. | +| livenessProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
LivenessProbe that will be added to CR pod | | logFormat#
_string_ | _(Optional)_
LogFormat for VMAlertmanager to be configured with. | | logLevel#
_string_ | _(Optional)_
Log level for VMAlertmanager to be configured with. | | managedMetadata#
_[ManagedObjectsMetadata](#managedobjectsmetadata)_ | _(Required)_
ManagedMetadata defines metadata that will be added to the all objects
created by operator for the given CustomResource | @@ -3554,6 +3576,7 @@ Appears in: [VMAlertmanager](#vmalertmanager) | portName#
_string_ | _(Optional)_
PortName used for the pods and governing service.
This defaults to web | | priorityClassName#
_string_ | _(Optional)_
PriorityClassName class assigned to the Pods | | readinessGates#
_[PodReadinessGate](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#podreadinessgate-v1-core) array_ | _(Required)_
ReadinessGates defines pod readiness gates | +| readinessProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
ReadinessProbe that will be added to CR pod | | replicaCount#
_integer_ | _(Optional)_
ReplicaCount is the expected size of the Application. | | resources#
_[ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#resourcerequirements-v1-core)_ | _(Optional)_
Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
if not defined default resources from operator config will be used | | retention#
_string_ | _(Optional)_
Retention Time duration VMAlertmanager shall retain data for. Default is '120h',
and must match the regular expression `[0-9]+(ms\|s\|m\|h)` (milliseconds seconds minutes hours). | @@ -3568,6 +3591,7 @@ Appears in: [VMAlertmanager](#vmalertmanager) | serviceAccountName#
_string_ | _(Optional)_
ServiceAccountName is the name of the ServiceAccount to use to run the pods | | serviceScrapeSpec#
_[VMServiceScrapeSpec](#vmservicescrapespec)_ | _(Optional)_
ServiceScrapeSpec that will be added to vmalertmanager VMServiceScrape spec | | serviceSpec#
_[AdditionalServiceSpec](#additionalservicespec)_ | _(Optional)_
ServiceSpec that will be added to vmalertmanager service spec | +| startupProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
StartupProbe that will be added to CR pod | | storage#
_[StorageSpec](#storagespec)_ | _(Optional)_
Storage is the definition of how storage will be used by the VMAlertmanager
instances. | | templates#
_[ConfigMapKeyReference](#configmapkeyreference) array_ | _(Optional)_
Templates is a list of ConfigMap key references for ConfigMaps in the same namespace as the VMAlertmanager
object, which shall be mounted into the VMAlertmanager Pods.
The Templates are mounted into /etc/vm/templates//. | | terminationGracePeriodSeconds#
_integer_ | _(Optional)_
TerminationGracePeriodSeconds period for container graceful termination | @@ -3666,6 +3690,7 @@ Appears in: [VMAuthLoadBalancer](#vmauthloadbalancer) | imagePullSecrets#
_[LocalObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#localobjectreference-v1-core) array_ | _(Optional)_
ImagePullSecrets An optional list of references to secrets in the same namespace
to use for pulling images from registries
see https://kubernetes.io/docs/concepts/containers/images/#referring-to-an-imagepullsecrets-on-a-pod | | initContainers#
_[Container](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#container-v1-core) array_ | _(Optional)_
InitContainers allows adding initContainers to the pod definition.
Any errors during the execution of an initContainer will lead to a restart of the Pod.
More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ | | license#
_[License](#license)_ | _(Optional)_
License configures enterprise features license key | +| livenessProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
LivenessProbe that will be added to CR pod | | logFormat#
_string_ | _(Optional)_
LogFormat for vmauth
default or json | | logLevel#
_string_ | _(Optional)_
LogLevel for vmauth container. | | minReadySeconds#
_integer_ | _(Optional)_
MinReadySeconds defines a minimum number of seconds to wait before starting update next pod
if previous in healthy state
Has no effect for VLogs and VMSingle | @@ -3676,6 +3701,7 @@ Appears in: [VMAuthLoadBalancer](#vmauthloadbalancer) | port#
_string_ | _(Optional)_
Port listen address | | priorityClassName#
_string_ | _(Optional)_
PriorityClassName class assigned to the Pods | | readinessGates#
_[PodReadinessGate](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#podreadinessgate-v1-core) array_ | _(Required)_
ReadinessGates defines pod readiness gates | +| readinessProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
ReadinessProbe that will be added to CR pod | | replicaCount#
_integer_ | _(Optional)_
ReplicaCount is the expected size of the Application. | | resources#
_[ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#resourcerequirements-v1-core)_ | _(Optional)_
Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
if not defined default resources from operator config will be used | | revisionHistoryLimitCount#
_integer_ | _(Optional)_
The number of old ReplicaSets to retain to allow rollback in deployment or
maximum number of revisions that will be maintained in the Deployment revision history.
Has no effect at StatefulSets
Defaults to 10. | @@ -3686,6 +3712,7 @@ Appears in: [VMAuthLoadBalancer](#vmauthloadbalancer) | securityContext#
_[SecurityContext](#securitycontext)_ | _(Optional)_
SecurityContext holds pod-level security attributes and common container settings.
This defaults to the default PodSecurityContext. | | serviceScrapeSpec#
_[VMServiceScrapeSpec](#vmservicescrapespec)_ | _(Optional)_
ServiceScrapeSpec that will be added to vmauthlb VMServiceScrape spec | | serviceSpec#
_[AdditionalServiceSpec](#additionalservicespec)_ | _(Required)_
AdditionalServiceSpec defines service override configuration for vmauth lb deployment
it'll be only applied to vmclusterlb- service | +| startupProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
StartupProbe that will be added to CR pod | | terminationGracePeriodSeconds#
_integer_ | _(Optional)_
TerminationGracePeriodSeconds period for container graceful termination | | tolerations#
_[Toleration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#toleration-v1-core) array_ | _(Optional)_
Tolerations If specified, the pod's tolerations. | | topologySpreadConstraints#
_[TopologySpreadConstraint](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#topologyspreadconstraint-v1-core) array_ | _(Optional)_
TopologySpreadConstraints embedded kubernetes pod configuration option,
controls how pods are spread across your cluster among failure-domains
such as regions, zones, nodes, and other user-defined topology domains
https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/ | @@ -3738,6 +3765,7 @@ Appears in: [VMAuth](#vmauth), [VMDistributedAuth](#vmdistributedauth) | internalListenPort#
_string_ | _(Optional)_
InternalListenPort instructs vmauth to serve internal routes at given port
available from v0.56.0 operator
and v1.111.0 vmauth version
related doc https://docs.victoriametrics.com/victoriametrics/vmauth/#security | | ip_filters#
_[VMUserIPFilters](#vmuseripfilters)_ | _(Optional)_
IPFilters defines per target src ip filters
supported only with enterprise version of [vmauth](https://docs.victoriametrics.com/victoriametrics/vmauth/#ip-filters) | | license#
_[License](#license)_ | _(Optional)_
License allows to configure license key to be used for enterprise features.
Using license key is supported starting from VictoriaMetrics v1.94.0.
See [here](https://docs.victoriametrics.com/victoriametrics/enterprise/) | +| livenessProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
LivenessProbe that will be added to CR pod | | load_balancing_policy#
_string_ | _(Optional)_
LoadBalancingPolicy defines load balancing policy to use for backend urls.
Supported policies: least_loaded, first_available.
See [here](https://docs.victoriametrics.com/victoriametrics/vmauth/#load-balancing) for more details (default "least_loaded") | | logFormat#
_string_ | _(Optional)_
LogFormat for VMAuth to be configured with. | | logLevel#
_string_ | _(Optional)_
LogLevel for victoria metrics single to be configured with. | @@ -3751,6 +3779,7 @@ Appears in: [VMAuth](#vmauth), [VMDistributedAuth](#vmdistributedauth) | port#
_string_ | _(Optional)_
Port listen address | | priorityClassName#
_string_ | _(Optional)_
PriorityClassName class assigned to the Pods | | readinessGates#
_[PodReadinessGate](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#podreadinessgate-v1-core) array_ | _(Required)_
ReadinessGates defines pod readiness gates | +| readinessProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
ReadinessProbe that will be added to CR pod | | replicaCount#
_integer_ | _(Optional)_
ReplicaCount is the expected size of the Application. | | resources#
_[ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#resourcerequirements-v1-core)_ | _(Optional)_
Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
if not defined default resources from operator config will be used | | response_headers#
_string array_ | _(Optional)_
ResponseHeaders represent additional http headers, that vmauth adds for request response
in form of ["header_key: header_value"]
multiple values for header key:
["header_key: value1,value2"]
it's available since 1.93.0 version of vmauth | @@ -3765,6 +3794,7 @@ Appears in: [VMAuth](#vmauth), [VMDistributedAuth](#vmdistributedauth) | serviceAccountName#
_string_ | _(Optional)_
ServiceAccountName is the name of the ServiceAccount to use to run the pods | | serviceScrapeSpec#
_[VMServiceScrapeSpec](#vmservicescrapespec)_ | _(Optional)_
ServiceScrapeSpec that will be added to vmauth VMServiceScrape spec | | serviceSpec#
_[AdditionalServiceSpec](#additionalservicespec)_ | _(Optional)_
ServiceSpec that will be added to vmsingle service spec | +| startupProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
StartupProbe that will be added to CR pod | | terminationGracePeriodSeconds#
_integer_ | _(Optional)_
TerminationGracePeriodSeconds period for container graceful termination | | tlsConfig#
_[TLSConfig](#tlsconfig)_ | _(Optional)_
TLSConfig defines tls configuration for the backend connection | | tolerations#
_[Toleration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#toleration-v1-core) array_ | _(Optional)_
Tolerations If specified, the pod's tolerations. | @@ -3895,6 +3925,7 @@ Appears in: [VMClusterSpec](#vmclusterspec) | imagePullSecrets#
_[LocalObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#localobjectreference-v1-core) array_ | _(Optional)_
ImagePullSecrets An optional list of references to secrets in the same namespace
to use for pulling images from registries
see https://kubernetes.io/docs/concepts/containers/images/#referring-to-an-imagepullsecrets-on-a-pod | | initContainers#
_[Container](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#container-v1-core) array_ | _(Optional)_
InitContainers allows adding initContainers to the pod definition.
Any errors during the execution of an initContainer will lead to a restart of the Pod.
More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ | | insertPorts#
_[InsertPorts](#insertports)_ | _(Required)_
InsertPorts - additional listen ports for data ingestion. | +| livenessProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
LivenessProbe that will be added to CR pod | | logFormat#
_string_ | _(Optional)_
LogFormat for VMInsert to be configured with.
default or json | | logLevel#
_string_ | _(Optional)_
LogLevel for VMInsert to be configured with. | | minReadySeconds#
_integer_ | _(Optional)_
MinReadySeconds defines a minimum number of seconds to wait before starting update next pod
if previous in healthy state
Has no effect for VLogs and VMSingle | @@ -3905,6 +3936,7 @@ Appears in: [VMClusterSpec](#vmclusterspec) | port#
_string_ | _(Optional)_
Port listen address | | priorityClassName#
_string_ | _(Optional)_
PriorityClassName class assigned to the Pods | | readinessGates#
_[PodReadinessGate](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#podreadinessgate-v1-core) array_ | _(Required)_
ReadinessGates defines pod readiness gates | +| readinessProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
ReadinessProbe that will be added to CR pod | | replicaCount#
_integer_ | _(Optional)_
ReplicaCount is the expected size of the Application. | | resources#
_[ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#resourcerequirements-v1-core)_ | _(Optional)_
Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
if not defined default resources from operator config will be used | | revisionHistoryLimitCount#
_integer_ | _(Optional)_
The number of old ReplicaSets to retain to allow rollback in deployment or
maximum number of revisions that will be maintained in the Deployment revision history.
Has no effect at StatefulSets
Defaults to 10. | @@ -3915,6 +3947,7 @@ Appears in: [VMClusterSpec](#vmclusterspec) | securityContext#
_[SecurityContext](#securitycontext)_ | _(Optional)_
SecurityContext holds pod-level security attributes and common container settings.
This defaults to the default PodSecurityContext. | | serviceScrapeSpec#
_[VMServiceScrapeSpec](#vmservicescrapespec)_ | _(Optional)_
ServiceScrapeSpec that will be added to vminsert VMServiceScrape spec | | serviceSpec#
_[AdditionalServiceSpec](#additionalservicespec)_ | _(Optional)_
ServiceSpec that will be added to vminsert service spec | +| startupProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
StartupProbe that will be added to CR pod | | terminationGracePeriodSeconds#
_integer_ | _(Optional)_
TerminationGracePeriodSeconds period for container graceful termination | | tolerations#
_[Toleration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#toleration-v1-core) array_ | _(Optional)_
Tolerations If specified, the pod's tolerations. | | topologySpreadConstraints#
_[TopologySpreadConstraint](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#topologyspreadconstraint-v1-core) array_ | _(Optional)_
TopologySpreadConstraints embedded kubernetes pod configuration option,
controls how pods are spread across your cluster among failure-domains
such as regions, zones, nodes, and other user-defined topology domains
https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/ | @@ -4245,6 +4278,7 @@ Appears in: [VMClusterSpec](#vmclusterspec) | image#
_[Image](#image)_ | _(Optional)_
Image - docker image settings
if no specified operator uses default version from operator config | | imagePullSecrets#
_[LocalObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#localobjectreference-v1-core) array_ | _(Optional)_
ImagePullSecrets An optional list of references to secrets in the same namespace
to use for pulling images from registries
see https://kubernetes.io/docs/concepts/containers/images/#referring-to-an-imagepullsecrets-on-a-pod | | initContainers#
_[Container](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#container-v1-core) array_ | _(Optional)_
InitContainers allows adding initContainers to the pod definition.
Any errors during the execution of an initContainer will lead to a restart of the Pod.
More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ | +| livenessProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
LivenessProbe that will be added to CR pod | | logFormat#
_string_ | _(Optional)_
LogFormat for VMSelect to be configured with.
default or json | | logLevel#
_string_ | _(Optional)_
LogLevel for VMSelect to be configured with. | | minReadySeconds#
_integer_ | _(Optional)_
MinReadySeconds defines a minimum number of seconds to wait before starting update next pod
if previous in healthy state
Has no effect for VLogs and VMSingle | @@ -4256,6 +4290,7 @@ Appears in: [VMClusterSpec](#vmclusterspec) | port#
_string_ | _(Optional)_
Port listen address | | priorityClassName#
_string_ | _(Optional)_
PriorityClassName class assigned to the Pods | | readinessGates#
_[PodReadinessGate](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#podreadinessgate-v1-core) array_ | _(Required)_
ReadinessGates defines pod readiness gates | +| readinessProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
ReadinessProbe that will be added to CR pod | | replicaCount#
_integer_ | _(Optional)_
ReplicaCount is the expected size of the Application. | | resources#
_[ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#resourcerequirements-v1-core)_ | _(Optional)_
Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
if not defined default resources from operator config will be used | | revisionHistoryLimitCount#
_integer_ | _(Optional)_
The number of old ReplicaSets to retain to allow rollback in deployment or
maximum number of revisions that will be maintained in the Deployment revision history.
Has no effect at StatefulSets
Defaults to 10. | @@ -4267,6 +4302,7 @@ Appears in: [VMClusterSpec](#vmclusterspec) | securityContext#
_[SecurityContext](#securitycontext)_ | _(Optional)_
SecurityContext holds pod-level security attributes and common container settings.
This defaults to the default PodSecurityContext. | | serviceScrapeSpec#
_[VMServiceScrapeSpec](#vmservicescrapespec)_ | _(Optional)_
ServiceScrapeSpec that will be added to vmselect VMServiceScrape spec | | serviceSpec#
_[AdditionalServiceSpec](#additionalservicespec)_ | _(Optional)_
ServiceSpec that will be added to vmselect service spec | +| startupProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
StartupProbe that will be added to CR pod | | storage#
_[StorageSpec](#storagespec)_ | _(Optional)_
StorageSpec - add persistent volume claim for cacheMountPath
its needed for persistent cache | | terminationGracePeriodSeconds#
_integer_ | _(Optional)_
TerminationGracePeriodSeconds period for container graceful termination | | tolerations#
_[Toleration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#toleration-v1-core) array_ | _(Optional)_
Tolerations If specified, the pod's tolerations. | @@ -4348,6 +4384,7 @@ Appears in: [VMSingle](#vmsingle) | initContainers#
_[Container](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#container-v1-core) array_ | _(Optional)_
InitContainers allows adding initContainers to the pod definition.
Any errors during the execution of an initContainer will lead to a restart of the Pod.
More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ | | insertPorts#
_[InsertPorts](#insertports)_ | _(Required)_
InsertPorts - additional listen ports for data ingestion. | | license#
_[License](#license)_ | _(Optional)_
License allows to configure license key to be used for enterprise features.
Using license key is supported starting from VictoriaMetrics v1.94.0.
See [here](https://docs.victoriametrics.com/victoriametrics/enterprise/) | +| livenessProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
LivenessProbe that will be added to CR pod | | logFormat#
_string_ | _(Optional)_
LogFormat for VMSingle to be configured with. | | logLevel#
_string_ | _(Optional)_
LogLevel for victoria metrics single to be configured with. | | managedMetadata#
_[ManagedObjectsMetadata](#managedobjectsmetadata)_ | _(Required)_
ManagedMetadata defines metadata that will be added to the all objects
created by operator for the given CustomResource | @@ -4358,6 +4395,7 @@ Appears in: [VMSingle](#vmsingle) | port#
_string_ | _(Optional)_
Port listen address | | priorityClassName#
_string_ | _(Optional)_
PriorityClassName class assigned to the Pods | | readinessGates#
_[PodReadinessGate](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#podreadinessgate-v1-core) array_ | _(Required)_
ReadinessGates defines pod readiness gates | +| readinessProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
ReadinessProbe that will be added to CR pod | | removePvcAfterDelete#
_boolean_ | _(Optional)_
RemovePvcAfterDelete - if true, controller adds ownership to pvc
and after VMSingle object deletion - pvc will be garbage collected
by controller manager | | replicaCount#
_integer_ | _(Optional)_
ReplicaCount is the expected size of the Application. | | resources#
_[ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#resourcerequirements-v1-core)_ | _(Optional)_
Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
if not defined default resources from operator config will be used | @@ -4370,6 +4408,7 @@ Appears in: [VMSingle](#vmsingle) | serviceAccountName#
_string_ | _(Optional)_
ServiceAccountName is the name of the ServiceAccount to use to run the pods | | serviceScrapeSpec#
_[VMServiceScrapeSpec](#vmservicescrapespec)_ | _(Optional)_
ServiceScrapeSpec that will be added to vmsingle VMServiceScrape spec | | serviceSpec#
_[AdditionalServiceSpec](#additionalservicespec)_ | _(Optional)_
ServiceSpec that will be added to vmsingle service spec | +| startupProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
StartupProbe that will be added to CR pod | | storage#
_[PersistentVolumeClaimSpec](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#persistentvolumeclaimspec-v1-core)_ | _(Optional)_
Storage is the definition of how storage will be used by the VMSingle
by default it`s empty dir
this option is ignored if storageDataPath is set | | storageDataPath#
_string_ | _(Optional)_
StorageDataPath disables spec.storage option and overrides arg for victoria-metrics binary --storageDataPath,
its users responsibility to mount proper device into given path.
It requires to provide spec.volumes and spec.volumeMounts with at least 1 value | | storageMetadata#
_[EmbeddedObjectMetadata](#embeddedobjectmetadata)_ | _(Optional)_
StorageMeta defines annotations and labels attached to PVC for given vmsingle CR | @@ -4432,6 +4471,7 @@ Appears in: [VMClusterSpec](#vmclusterspec) | image#
_[Image](#image)_ | _(Optional)_
Image - docker image settings
if no specified operator uses default version from operator config | | imagePullSecrets#
_[LocalObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#localobjectreference-v1-core) array_ | _(Optional)_
ImagePullSecrets An optional list of references to secrets in the same namespace
to use for pulling images from registries
see https://kubernetes.io/docs/concepts/containers/images/#referring-to-an-imagepullsecrets-on-a-pod | | initContainers#
_[Container](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#container-v1-core) array_ | _(Optional)_
InitContainers allows adding initContainers to the pod definition.
Any errors during the execution of an initContainer will lead to a restart of the Pod.
More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ | +| livenessProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
LivenessProbe that will be added to CR pod | | logFormat#
_string_ | _(Optional)_
LogFormat for VMStorage to be configured with.
default or json | | logLevel#
_string_ | _(Optional)_
LogLevel for VMStorage to be configured with. | | maintenanceInsertNodeIDs#
_integer array_ | _(Optional)_
MaintenanceInsertNodeIDs - excludes given node ids from insert requests routing, must contain pod suffixes - for pod-0, id will be 0 and etc.
lets say, you have pod-0, pod-1, pod-2, pod-3. to exclude pod-0 and pod-3 from insert routing, define nodeIDs: [0,3].
Useful at storage expanding, when you want to rebalance some data at cluster. | @@ -4445,6 +4485,7 @@ Appears in: [VMClusterSpec](#vmclusterspec) | port#
_string_ | _(Optional)_
Port listen address | | priorityClassName#
_string_ | _(Optional)_
PriorityClassName class assigned to the Pods | | readinessGates#
_[PodReadinessGate](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#podreadinessgate-v1-core) array_ | _(Required)_
ReadinessGates defines pod readiness gates | +| readinessProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
ReadinessProbe that will be added to CR pod | | replicaCount#
_integer_ | _(Optional)_
ReplicaCount is the expected size of the Application. | | resources#
_[ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#resourcerequirements-v1-core)_ | _(Optional)_
Resources container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
if not defined default resources from operator config will be used | | revisionHistoryLimitCount#
_integer_ | _(Optional)_
The number of old ReplicaSets to retain to allow rollback in deployment or
maximum number of revisions that will be maintained in the Deployment revision history.
Has no effect at StatefulSets
Defaults to 10. | @@ -4456,6 +4497,7 @@ Appears in: [VMClusterSpec](#vmclusterspec) | securityContext#
_[SecurityContext](#securitycontext)_ | _(Optional)_
SecurityContext holds pod-level security attributes and common container settings.
This defaults to the default PodSecurityContext. | | serviceScrapeSpec#
_[VMServiceScrapeSpec](#vmservicescrapespec)_ | _(Optional)_
ServiceScrapeSpec that will be added to vmstorage VMServiceScrape spec | | serviceSpec#
_[AdditionalServiceSpec](#additionalservicespec)_ | _(Optional)_
ServiceSpec that will be create additional service for vmstorage | +| startupProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
StartupProbe that will be added to CR pod | | storage#
_[StorageSpec](#storagespec)_ | _(Optional)_
Storage - add persistent volume for StorageDataPath
its useful for persistent cache | | storageDataPath#
_string_ | _(Optional)_
StorageDataPath - path to storage data | | terminationGracePeriodSeconds#
_integer_ | _(Optional)_
TerminationGracePeriodSeconds period for container graceful termination | diff --git a/docs/env.md b/docs/env.md index d682c4195..36fcd3713 100644 --- a/docs/env.md +++ b/docs/env.md @@ -30,6 +30,7 @@ | VM_VLOGSDEFAULT_RESOURCE_REQUEST_MEM: `500Mi` # | | VM_VLOGSDEFAULT_RESOURCE_REQUEST_CPU: `150m` # | | VM_VLOGSDEFAULT_RESOURCE_REQUEST_EPHEMERAL_STORAGE: `unlimited` # | +| VM_VLOGSDEFAULT_TERMINATION_GRACE_PERIOD_SECONDS: `30` # | | VM_VLAGENTDEFAULT_IMAGE: `victoriametrics/vlagent` # | | VM_VLAGENTDEFAULT_VERSION: `${VM_LOGS_VERSION}` # | | VM_VLAGENTDEFAULT_PORT: `9429` # | @@ -40,6 +41,7 @@ | VM_VLAGENTDEFAULT_RESOURCE_REQUEST_MEM: `200Mi` # | | VM_VLAGENTDEFAULT_RESOURCE_REQUEST_CPU: `50m` # | | VM_VLAGENTDEFAULT_RESOURCE_REQUEST_EPHEMERAL_STORAGE: `unlimited` # | +| VM_VLAGENTDEFAULT_TERMINATION_GRACE_PERIOD_SECONDS: `30` # | | VM_VLSINGLEDEFAULT_IMAGE: `victoriametrics/victoria-logs` # | | VM_VLSINGLEDEFAULT_VERSION: `${VM_LOGS_VERSION}` # | | VM_VLSINGLEDEFAULT_PORT: `9428` # | @@ -50,6 +52,7 @@ | VM_VLSINGLEDEFAULT_RESOURCE_REQUEST_MEM: `500Mi` # | | VM_VLSINGLEDEFAULT_RESOURCE_REQUEST_CPU: `150m` # | | VM_VLSINGLEDEFAULT_RESOURCE_REQUEST_EPHEMERAL_STORAGE: `unlimited` # | +| VM_VLSINGLEDEFAULT_TERMINATION_GRACE_PERIOD_SECONDS: `30` # | | VM_VTSINGLEDEFAULT_IMAGE: `victoriametrics/victoria-traces` # | | VM_VTSINGLEDEFAULT_VERSION: `${VM_TRACES_VERSION}` # | | VM_VTSINGLEDEFAULT_PORT: `10428` # | @@ -60,6 +63,7 @@ | VM_VTSINGLEDEFAULT_RESOURCE_REQUEST_MEM: `500Mi` # | | VM_VTSINGLEDEFAULT_RESOURCE_REQUEST_CPU: `150m` # | | VM_VTSINGLEDEFAULT_RESOURCE_REQUEST_EPHEMERAL_STORAGE: `unlimited` # | +| VM_VTSINGLEDEFAULT_TERMINATION_GRACE_PERIOD_SECONDS: `30` # | | VM_VMALERTDEFAULT_IMAGE: `victoriametrics/vmalert` # | | VM_VMALERTDEFAULT_VERSION: `${VM_METRICS_VERSION}` # | | VM_VMALERTDEFAULT_PORT: `8080` # | @@ -70,6 +74,7 @@ | VM_VMALERTDEFAULT_RESOURCE_REQUEST_MEM: `200Mi` # | | VM_VMALERTDEFAULT_RESOURCE_REQUEST_CPU: `50m` # | | VM_VMALERTDEFAULT_RESOURCE_REQUEST_EPHEMERAL_STORAGE: `unlimited` # | +| VM_VMALERTDEFAULT_TERMINATION_GRACE_PERIOD_SECONDS: `30` # | | VM_VMSERVICESCRAPEDEFAULT_ENFORCEENDPOINTSLICES: `false` #
Use endpointslices instead of endpoints as discovery role for vmservicescrape when generate scrape config for vmagent. | | VM_VMAGENTDEFAULT_IMAGE: `victoriametrics/vmagent` # | | VM_VMAGENTDEFAULT_VERSION: `${VM_METRICS_VERSION}` # | @@ -81,6 +86,7 @@ | VM_VMAGENTDEFAULT_RESOURCE_REQUEST_MEM: `200Mi` # | | VM_VMAGENTDEFAULT_RESOURCE_REQUEST_CPU: `50m` # | | VM_VMAGENTDEFAULT_RESOURCE_REQUEST_EPHEMERAL_STORAGE: `unlimited` # | +| VM_VMAGENTDEFAULT_TERMINATION_GRACE_PERIOD_SECONDS: `30` # | | VM_VMANOMALYDEFAULT_IMAGE: `victoriametrics/vmanomaly` # | | VM_VMANOMALYDEFAULT_VERSION: `${VM_ANOMALY_VERSION}` # | | VM_VMANOMALYDEFAULT_PORT: `8490` # | @@ -91,6 +97,7 @@ | VM_VMANOMALYDEFAULT_RESOURCE_REQUEST_MEM: `200Mi` # | | VM_VMANOMALYDEFAULT_RESOURCE_REQUEST_CPU: `50m` # | | VM_VMANOMALYDEFAULT_RESOURCE_REQUEST_EPHEMERAL_STORAGE: `unlimited` # | +| VM_VMANOMALYDEFAULT_TERMINATION_GRACE_PERIOD_SECONDS: `30` # | | VM_VMSINGLEDEFAULT_IMAGE: `victoriametrics/victoria-metrics` # | | VM_VMSINGLEDEFAULT_VERSION: `${VM_METRICS_VERSION}` # | | VM_VMSINGLEDEFAULT_PORT: `8429` # | @@ -101,36 +108,43 @@ | VM_VMSINGLEDEFAULT_RESOURCE_REQUEST_MEM: `500Mi` # | | VM_VMSINGLEDEFAULT_RESOURCE_REQUEST_CPU: `150m` # | | VM_VMSINGLEDEFAULT_RESOURCE_REQUEST_EPHEMERAL_STORAGE: `unlimited` # | +| VM_VMSINGLEDEFAULT_TERMINATION_GRACE_PERIOD_SECONDS: `30` # | | VM_VMCLUSTERDEFAULT_USEDEFAULTRESOURCES: `true` # | | VM_VMCLUSTERDEFAULT_VMSELECTDEFAULT_IMAGE: `victoriametrics/vmselect` # | | VM_VMCLUSTERDEFAULT_VMSELECTDEFAULT_VERSION: `${VM_METRICS_VERSION}-cluster` # | | VM_VMCLUSTERDEFAULT_VMSELECTDEFAULT_PORT: `8481` # | +| VM_VMCLUSTERDEFAULT_VMSELECTDEFAULT_USEDEFAULTRESOURCES: `${VM_VMCLUSTERDEFAULT_USEDEFAULTRESOURCES}` # | | VM_VMCLUSTERDEFAULT_VMSELECTDEFAULT_RESOURCE_LIMIT_MEM: `1000Mi` # | | VM_VMCLUSTERDEFAULT_VMSELECTDEFAULT_RESOURCE_LIMIT_CPU: `500m` # | | VM_VMCLUSTERDEFAULT_VMSELECTDEFAULT_RESOURCE_LIMIT_EPHEMERAL_STORAGE: `unlimited` # | | VM_VMCLUSTERDEFAULT_VMSELECTDEFAULT_RESOURCE_REQUEST_MEM: `500Mi` # | | VM_VMCLUSTERDEFAULT_VMSELECTDEFAULT_RESOURCE_REQUEST_CPU: `100m` # | | VM_VMCLUSTERDEFAULT_VMSELECTDEFAULT_RESOURCE_REQUEST_EPHEMERAL_STORAGE: `unlimited` # | -| VM_VMCLUSTERDEFAULT_VMSTORAGEDEFAULT_IMAGE: `victoriametrics/vmstorage` # | -| VM_VMCLUSTERDEFAULT_VMSTORAGEDEFAULT_VERSION: `${VM_METRICS_VERSION}-cluster` # | +| VM_VMCLUSTERDEFAULT_VMSELECTDEFAULT_TERMINATION_GRACE_PERIOD_SECONDS: `30` # | | VM_VMCLUSTERDEFAULT_VMSTORAGEDEFAULT_VMINSERTPORT: `8400` # | | VM_VMCLUSTERDEFAULT_VMSTORAGEDEFAULT_VMSELECTPORT: `8401` # | +| VM_VMCLUSTERDEFAULT_VMSTORAGEDEFAULT_IMAGE: `victoriametrics/vmstorage` # | +| VM_VMCLUSTERDEFAULT_VMSTORAGEDEFAULT_VERSION: `${VM_METRICS_VERSION}-cluster` # | | VM_VMCLUSTERDEFAULT_VMSTORAGEDEFAULT_PORT: `8482` # | +| VM_VMCLUSTERDEFAULT_VMSTORAGEDEFAULT_USEDEFAULTRESOURCES: `${VM_VMCLUSTERDEFAULT_USEDEFAULTRESOURCES}` # | | VM_VMCLUSTERDEFAULT_VMSTORAGEDEFAULT_RESOURCE_LIMIT_MEM: `1500Mi` # | | VM_VMCLUSTERDEFAULT_VMSTORAGEDEFAULT_RESOURCE_LIMIT_CPU: `1000m` # | | VM_VMCLUSTERDEFAULT_VMSTORAGEDEFAULT_RESOURCE_LIMIT_EPHEMERAL_STORAGE: `unlimited` # | | VM_VMCLUSTERDEFAULT_VMSTORAGEDEFAULT_RESOURCE_REQUEST_MEM: `500Mi` # | | VM_VMCLUSTERDEFAULT_VMSTORAGEDEFAULT_RESOURCE_REQUEST_CPU: `250m` # | | VM_VMCLUSTERDEFAULT_VMSTORAGEDEFAULT_RESOURCE_REQUEST_EPHEMERAL_STORAGE: `unlimited` # | +| VM_VMCLUSTERDEFAULT_VMSTORAGEDEFAULT_TERMINATION_GRACE_PERIOD_SECONDS: `30` # | | VM_VMCLUSTERDEFAULT_VMINSERTDEFAULT_IMAGE: `victoriametrics/vminsert` # | | VM_VMCLUSTERDEFAULT_VMINSERTDEFAULT_VERSION: `${VM_METRICS_VERSION}-cluster` # | | VM_VMCLUSTERDEFAULT_VMINSERTDEFAULT_PORT: `8480` # | +| VM_VMCLUSTERDEFAULT_VMINSERTDEFAULT_USEDEFAULTRESOURCES: `${VM_VMCLUSTERDEFAULT_USEDEFAULTRESOURCES}` # | | VM_VMCLUSTERDEFAULT_VMINSERTDEFAULT_RESOURCE_LIMIT_MEM: `500Mi` # | | VM_VMCLUSTERDEFAULT_VMINSERTDEFAULT_RESOURCE_LIMIT_CPU: `500m` # | | VM_VMCLUSTERDEFAULT_VMINSERTDEFAULT_RESOURCE_LIMIT_EPHEMERAL_STORAGE: `unlimited` # | | VM_VMCLUSTERDEFAULT_VMINSERTDEFAULT_RESOURCE_REQUEST_MEM: `200Mi` # | | VM_VMCLUSTERDEFAULT_VMINSERTDEFAULT_RESOURCE_REQUEST_CPU: `150m` # | | VM_VMCLUSTERDEFAULT_VMINSERTDEFAULT_RESOURCE_REQUEST_EPHEMERAL_STORAGE: `unlimited` # | +| VM_VMCLUSTERDEFAULT_VMINSERTDEFAULT_TERMINATION_GRACE_PERIOD_SECONDS: `30` # | | VM_VMALERTMANAGER_ALERTMANAGERDEFAULTBASEIMAGE: `prom/alertmanager` # | | VM_VMALERTMANAGER_ALERTMANAGERVERSION: `v0.31.0` # | | VM_VMALERTMANAGER_PORT: `9093` # | @@ -141,6 +155,7 @@ | VM_VMALERTMANAGER_RESOURCE_REQUEST_MEM: `56Mi` # | | VM_VMALERTMANAGER_RESOURCE_REQUEST_CPU: `30m` # | | VM_VMALERTMANAGER_RESOURCE_REQUEST_EPHEMERAL_STORAGE: `unlimited` # | +| VM_VMALERTMANAGER_TERMINATION_GRACE_PERIOD_SECONDS: `120` # | | VM_DISABLESELFSERVICESCRAPECREATION: `false` # | | VM_VMBACKUP_IMAGE: `victoriametrics/vmbackupmanager` # | | VM_VMBACKUP_VERSION: `${VM_METRICS_VERSION}-enterprise` # | @@ -152,6 +167,7 @@ | VM_VMBACKUP_RESOURCE_REQUEST_MEM: `200Mi` # | | VM_VMBACKUP_RESOURCE_REQUEST_CPU: `150m` # | | VM_VMBACKUP_RESOURCE_REQUEST_EPHEMERAL_STORAGE: `unlimited` # | +| VM_VMBACKUP_TERMINATION_GRACE_PERIOD_SECONDS: `30` # | | VM_VMAUTHDEFAULT_IMAGE: `victoriametrics/vmauth` # | | VM_VMAUTHDEFAULT_VERSION: `${VM_METRICS_VERSION}` # | | VM_VMAUTHDEFAULT_PORT: `8427` # | @@ -162,62 +178,75 @@ | VM_VMAUTHDEFAULT_RESOURCE_REQUEST_MEM: `100Mi` # | | VM_VMAUTHDEFAULT_RESOURCE_REQUEST_CPU: `50m` # | | VM_VMAUTHDEFAULT_RESOURCE_REQUEST_EPHEMERAL_STORAGE: `unlimited` # | +| VM_VMAUTHDEFAULT_TERMINATION_GRACE_PERIOD_SECONDS: `30` # | | VM_VLCLUSTERDEFAULT_USEDEFAULTRESOURCES: `true` # | | VM_VLCLUSTERDEFAULT_VLSELECTDEFAULT_IMAGE: `victoriametrics/victoria-logs` # | | VM_VLCLUSTERDEFAULT_VLSELECTDEFAULT_VERSION: `${VM_LOGS_VERSION}` # | | VM_VLCLUSTERDEFAULT_VLSELECTDEFAULT_PORT: `9471` # | +| VM_VLCLUSTERDEFAULT_VLSELECTDEFAULT_USEDEFAULTRESOURCES: `${VM_VLCLUSTERDEFAULT_USEDEFAULTRESOURCES}` # | | VM_VLCLUSTERDEFAULT_VLSELECTDEFAULT_RESOURCE_LIMIT_MEM: `1024Mi` # | | VM_VLCLUSTERDEFAULT_VLSELECTDEFAULT_RESOURCE_LIMIT_CPU: `1000m` # | | VM_VLCLUSTERDEFAULT_VLSELECTDEFAULT_RESOURCE_LIMIT_EPHEMERAL_STORAGE: `unlimited` # | | VM_VLCLUSTERDEFAULT_VLSELECTDEFAULT_RESOURCE_REQUEST_MEM: `256Mi` # | | VM_VLCLUSTERDEFAULT_VLSELECTDEFAULT_RESOURCE_REQUEST_CPU: `100m` # | | VM_VLCLUSTERDEFAULT_VLSELECTDEFAULT_RESOURCE_REQUEST_EPHEMERAL_STORAGE: `unlimited` # | +| VM_VLCLUSTERDEFAULT_VLSELECTDEFAULT_TERMINATION_GRACE_PERIOD_SECONDS: `30` # | | VM_VLCLUSTERDEFAULT_VLSTORAGEDEFAULT_IMAGE: `victoriametrics/victoria-logs` # | | VM_VLCLUSTERDEFAULT_VLSTORAGEDEFAULT_VERSION: `${VM_LOGS_VERSION}` # | | VM_VLCLUSTERDEFAULT_VLSTORAGEDEFAULT_PORT: `9491` # | +| VM_VLCLUSTERDEFAULT_VLSTORAGEDEFAULT_USEDEFAULTRESOURCES: `${VM_VLCLUSTERDEFAULT_USEDEFAULTRESOURCES}` # | | VM_VLCLUSTERDEFAULT_VLSTORAGEDEFAULT_RESOURCE_LIMIT_MEM: `2048Mi` # | | VM_VLCLUSTERDEFAULT_VLSTORAGEDEFAULT_RESOURCE_LIMIT_CPU: `1000m` # | | VM_VLCLUSTERDEFAULT_VLSTORAGEDEFAULT_RESOURCE_LIMIT_EPHEMERAL_STORAGE: `unlimited` # | | VM_VLCLUSTERDEFAULT_VLSTORAGEDEFAULT_RESOURCE_REQUEST_MEM: `512Mi` # | | VM_VLCLUSTERDEFAULT_VLSTORAGEDEFAULT_RESOURCE_REQUEST_CPU: `200m` # | | VM_VLCLUSTERDEFAULT_VLSTORAGEDEFAULT_RESOURCE_REQUEST_EPHEMERAL_STORAGE: `unlimited` # | +| VM_VLCLUSTERDEFAULT_VLSTORAGEDEFAULT_TERMINATION_GRACE_PERIOD_SECONDS: `30` # | | VM_VLCLUSTERDEFAULT_VLINSERTDEFAULT_IMAGE: `victoriametrics/victoria-logs` # | | VM_VLCLUSTERDEFAULT_VLINSERTDEFAULT_VERSION: `${VM_LOGS_VERSION}` # | | VM_VLCLUSTERDEFAULT_VLINSERTDEFAULT_PORT: `9481` # | +| VM_VLCLUSTERDEFAULT_VLINSERTDEFAULT_USEDEFAULTRESOURCES: `${VM_VLCLUSTERDEFAULT_USEDEFAULTRESOURCES}` # | | VM_VLCLUSTERDEFAULT_VLINSERTDEFAULT_RESOURCE_LIMIT_MEM: `1024Mi` # | | VM_VLCLUSTERDEFAULT_VLINSERTDEFAULT_RESOURCE_LIMIT_CPU: `1000m` # | | VM_VLCLUSTERDEFAULT_VLINSERTDEFAULT_RESOURCE_LIMIT_EPHEMERAL_STORAGE: `unlimited` # | | VM_VLCLUSTERDEFAULT_VLINSERTDEFAULT_RESOURCE_REQUEST_MEM: `256Mi` # | | VM_VLCLUSTERDEFAULT_VLINSERTDEFAULT_RESOURCE_REQUEST_CPU: `100m` # | | VM_VLCLUSTERDEFAULT_VLINSERTDEFAULT_RESOURCE_REQUEST_EPHEMERAL_STORAGE: `unlimited` # | +| VM_VLCLUSTERDEFAULT_VLINSERTDEFAULT_TERMINATION_GRACE_PERIOD_SECONDS: `30` # | | VM_VTCLUSTERDEFAULT_USEDEFAULTRESOURCES: `true` # | | VM_VTCLUSTERDEFAULT_SELECT_IMAGE: `victoriametrics/victoria-traces` # | | VM_VTCLUSTERDEFAULT_SELECT_VERSION: `${VM_TRACES_VERSION}` # | | VM_VTCLUSTERDEFAULT_SELECT_PORT: `10471` # | +| VM_VTCLUSTERDEFAULT_SELECT_USEDEFAULTRESOURCES: `${VM_VTCLUSTERDEFAULT_USEDEFAULTRESOURCES}` # | | VM_VTCLUSTERDEFAULT_SELECT_RESOURCE_LIMIT_MEM: `1024Mi` # | | VM_VTCLUSTERDEFAULT_SELECT_RESOURCE_LIMIT_CPU: `1000m` # | | VM_VTCLUSTERDEFAULT_SELECT_RESOURCE_LIMIT_EPHEMERAL_STORAGE: `unlimited` # | | VM_VTCLUSTERDEFAULT_SELECT_RESOURCE_REQUEST_MEM: `256Mi` # | | VM_VTCLUSTERDEFAULT_SELECT_RESOURCE_REQUEST_CPU: `100m` # | | VM_VTCLUSTERDEFAULT_SELECT_RESOURCE_REQUEST_EPHEMERAL_STORAGE: `unlimited` # | +| VM_VTCLUSTERDEFAULT_SELECT_TERMINATION_GRACE_PERIOD_SECONDS: `30` # | | VM_VTCLUSTERDEFAULT_STORAGE_IMAGE: `victoriametrics/victoria-traces` # | | VM_VTCLUSTERDEFAULT_STORAGE_VERSION: `${VM_TRACES_VERSION}` # | | VM_VTCLUSTERDEFAULT_STORAGE_PORT: `10491` # | +| VM_VTCLUSTERDEFAULT_STORAGE_USEDEFAULTRESOURCES: `${VM_VTCLUSTERDEFAULT_USEDEFAULTRESOURCES}` # | | VM_VTCLUSTERDEFAULT_STORAGE_RESOURCE_LIMIT_MEM: `2048Mi` # | | VM_VTCLUSTERDEFAULT_STORAGE_RESOURCE_LIMIT_CPU: `1000m` # | | VM_VTCLUSTERDEFAULT_STORAGE_RESOURCE_LIMIT_EPHEMERAL_STORAGE: `unlimited` # | | VM_VTCLUSTERDEFAULT_STORAGE_RESOURCE_REQUEST_MEM: `512Mi` # | | VM_VTCLUSTERDEFAULT_STORAGE_RESOURCE_REQUEST_CPU: `200m` # | | VM_VTCLUSTERDEFAULT_STORAGE_RESOURCE_REQUEST_EPHEMERAL_STORAGE: `unlimited` # | +| VM_VTCLUSTERDEFAULT_STORAGE_TERMINATION_GRACE_PERIOD_SECONDS: `30` # | | VM_VTCLUSTERDEFAULT_INSERT_IMAGE: `victoriametrics/victoria-traces` # | | VM_VTCLUSTERDEFAULT_INSERT_VERSION: `${VM_TRACES_VERSION}` # | | VM_VTCLUSTERDEFAULT_INSERT_PORT: `10481` # | +| VM_VTCLUSTERDEFAULT_INSERT_USEDEFAULTRESOURCES: `${VM_VTCLUSTERDEFAULT_USEDEFAULTRESOURCES}` # | | VM_VTCLUSTERDEFAULT_INSERT_RESOURCE_LIMIT_MEM: `1024Mi` # | | VM_VTCLUSTERDEFAULT_INSERT_RESOURCE_LIMIT_CPU: `1000m` # | | VM_VTCLUSTERDEFAULT_INSERT_RESOURCE_LIMIT_EPHEMERAL_STORAGE: `unlimited` # | | VM_VTCLUSTERDEFAULT_INSERT_RESOURCE_REQUEST_MEM: `256Mi` # | | VM_VTCLUSTERDEFAULT_INSERT_RESOURCE_REQUEST_CPU: `100m` # | | VM_VTCLUSTERDEFAULT_INSERT_RESOURCE_REQUEST_EPHEMERAL_STORAGE: `unlimited` # | +| VM_VTCLUSTERDEFAULT_INSERT_TERMINATION_GRACE_PERIOD_SECONDS: `30` # | | VM_ENABLEDPROMETHEUSCONVERTER_PODMONITOR: `true` # | | VM_ENABLEDPROMETHEUSCONVERTER_SERVICESCRAPE: `true` # | | VM_ENABLEDPROMETHEUSCONVERTER_PROMETHEUSRULE: `true` # | diff --git a/go.mod b/go.mod index c5ee4f80e..210400d00 100644 --- a/go.mod +++ b/go.mod @@ -125,3 +125,5 @@ require ( ) replace github.com/VictoriaMetrics/operator/api => ./api + +replace github.com/caarlos0/env/v11 => github.com/AndrewChubatiuk/env/v11 v11.0.0-20260302065400-14d0354881b6 diff --git a/go.sum b/go.sum index 1ed8676aa..e87561340 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/AndrewChubatiuk/env/v11 v11.0.0-20260302065400-14d0354881b6 h1:5CPOPjp7co7TgffUQ/jOVlw6IX8uHXDHt0W85Mwd7Zw= +github.com/AndrewChubatiuk/env/v11 v11.0.0-20260302065400-14d0354881b6/go.mod h1:qupehSf/Y0TUTsxKywqRt/vJjN5nz6vauiYEUUr8P4U= github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/VictoriaMetrics/VictoriaLogs v1.36.2-0.20251008164716-21c0fb3de84d h1:fV15mhBCGpCCBbuOAbOflO8Air+tLklMt8bG35FimzQ= @@ -42,8 +44,6 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bmatcuk/doublestar/v4 v4.9.1 h1:X8jg9rRZmJd4yRy7ZeNDRnM+T3ZfHv15JiBJ/avrEXE= github.com/bmatcuk/doublestar/v4 v4.9.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= -github.com/caarlos0/env/v11 v11.3.1 h1:cArPWC15hWmEt+gWk7YBi7lEXTXCvpaSdCiZE2X5mCA= -github.com/caarlos0/env/v11 v11.3.1/go.mod h1:qupehSf/Y0TUTsxKywqRt/vJjN5nz6vauiYEUUr8P4U= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/internal/config/config.go b/internal/config/config.go index 2343fe37b..0a5425b7a 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -92,6 +92,7 @@ type ApplicationDefaults struct { EphemeralStorage string } } + TerminationGracePeriodSeconds int64 } //genvars:true @@ -159,6 +160,7 @@ type BaseOperatorConf struct { EphemeralStorage string `default:"unlimited"` } `prefix:"REQUEST_"` } `prefix:"RESOURCE_"` + TerminationGracePeriodSeconds int64 `default:"30"` } `prefix:"VM_VLOGSDEFAULT_"` VLAgent struct { @@ -178,6 +180,7 @@ type BaseOperatorConf struct { EphemeralStorage string `default:"unlimited"` } `prefix:"REQUEST_"` } `prefix:"RESOURCE_"` + TerminationGracePeriodSeconds int64 `default:"30"` } `prefix:"VM_VLAGENTDEFAULT_"` VLSingle struct { @@ -197,6 +200,7 @@ type BaseOperatorConf struct { EphemeralStorage string `default:"unlimited"` } `prefix:"REQUEST_"` } `prefix:"RESOURCE_"` + TerminationGracePeriodSeconds int64 `default:"30"` } `prefix:"VM_VLSINGLEDEFAULT_"` VTSingle struct { @@ -216,6 +220,7 @@ type BaseOperatorConf struct { EphemeralStorage string `default:"unlimited"` } `prefix:"REQUEST_"` } `prefix:"RESOURCE_"` + TerminationGracePeriodSeconds int64 `default:"30"` } `prefix:"VM_VTSINGLEDEFAULT_"` VMAlert struct { @@ -235,6 +240,7 @@ type BaseOperatorConf struct { EphemeralStorage string `default:"unlimited"` } `prefix:"REQUEST_"` } `prefix:"RESOURCE_"` + TerminationGracePeriodSeconds int64 `default:"30"` } `prefix:"VM_VMALERTDEFAULT_"` VMServiceScrape struct { @@ -260,6 +266,7 @@ type BaseOperatorConf struct { EphemeralStorage string `default:"unlimited"` } `prefix:"REQUEST_"` } `prefix:"RESOURCE_"` + TerminationGracePeriodSeconds int64 `default:"30"` } `prefix:"VM_VMAGENTDEFAULT_"` VMAnomaly struct { @@ -279,6 +286,7 @@ type BaseOperatorConf struct { EphemeralStorage string `default:"unlimited"` } `prefix:"REQUEST_"` } `prefix:"RESOURCE_"` + TerminationGracePeriodSeconds int64 `default:"30"` } `prefix:"VM_VMANOMALYDEFAULT_"` VMSingle struct { @@ -298,15 +306,17 @@ type BaseOperatorConf struct { EphemeralStorage string `default:"unlimited"` } `prefix:"REQUEST_"` } `prefix:"RESOURCE_"` + TerminationGracePeriodSeconds int64 `default:"30"` } `prefix:"VM_VMSINGLEDEFAULT_"` VMCluster struct { UseDefaultResources bool `default:"true" env:"USEDEFAULTRESOURCES"` Select struct { - Image string `default:"victoriametrics/vmselect"` - Version string `env:",expand" default:"${VM_METRICS_VERSION}-cluster"` - Port string `default:"8481"` - Resource struct { + Image string `default:"victoriametrics/vmselect"` + Version string `env:",expand" default:"${VM_METRICS_VERSION}-cluster"` + Port string `default:"8481"` + UseDefaultResources bool `env:"USEDEFAULTRESOURCES,expand" default:"${VM_VMCLUSTERDEFAULT_USEDEFAULTRESOURCES}"` + Resource struct { Limit struct { Mem string `default:"1000Mi"` Cpu string `default:"500m"` @@ -318,31 +328,37 @@ type BaseOperatorConf struct { EphemeralStorage string `default:"unlimited"` } `prefix:"REQUEST_"` } `prefix:"RESOURCE_"` + TerminationGracePeriodSeconds int64 `default:"30"` } `prefix:"VMSELECTDEFAULT_"` Storage struct { - Image string `default:"victoriametrics/vmstorage"` - Version string `env:",expand" default:"${VM_METRICS_VERSION}-cluster"` VMInsertPort string `default:"8400" env:"VMINSERTPORT"` VMSelectPort string `default:"8401" env:"VMSELECTPORT"` - Port string `default:"8482"` - Resource struct { - Limit struct { - Mem string `default:"1500Mi"` - Cpu string `default:"1000m"` - EphemeralStorage string `default:"unlimited"` - } `prefix:"LIMIT_"` - Request struct { - Mem string `default:"500Mi"` - Cpu string `default:"250m"` - EphemeralStorage string `default:"unlimited"` - } `prefix:"REQUEST_"` - } `prefix:"RESOURCE_"` + Common struct { + Image string `default:"victoriametrics/vmstorage"` + Version string `env:",expand" default:"${VM_METRICS_VERSION}-cluster"` + Port string `default:"8482"` + UseDefaultResources bool `env:"USEDEFAULTRESOURCES,expand" default:"${VM_VMCLUSTERDEFAULT_USEDEFAULTRESOURCES}"` + Resource struct { + Limit struct { + Mem string `default:"1500Mi"` + Cpu string `default:"1000m"` + EphemeralStorage string `default:"unlimited"` + } `prefix:"LIMIT_"` + Request struct { + Mem string `default:"500Mi"` + Cpu string `default:"250m"` + EphemeralStorage string `default:"unlimited"` + } `prefix:"REQUEST_"` + } `prefix:"RESOURCE_"` + TerminationGracePeriodSeconds int64 `default:"30"` + } } `prefix:"VMSTORAGEDEFAULT_"` Insert struct { - Image string `default:"victoriametrics/vminsert"` - Version string `env:",expand" default:"${VM_METRICS_VERSION}-cluster"` - Port string `default:"8480"` - Resource struct { + Image string `default:"victoriametrics/vminsert"` + Version string `env:",expand" default:"${VM_METRICS_VERSION}-cluster"` + Port string `default:"8480"` + UseDefaultResources bool `env:"USEDEFAULTRESOURCES,expand" default:"${VM_VMCLUSTERDEFAULT_USEDEFAULTRESOURCES}"` + Resource struct { Limit struct { Mem string `default:"500Mi"` Cpu string `default:"500m"` @@ -354,6 +370,7 @@ type BaseOperatorConf struct { EphemeralStorage string `default:"unlimited"` } `prefix:"REQUEST_"` } `prefix:"RESOURCE_"` + TerminationGracePeriodSeconds int64 `default:"30"` } `prefix:"VMINSERTDEFAULT_"` } `prefix:"VM_VMCLUSTERDEFAULT_"` @@ -374,6 +391,7 @@ type BaseOperatorConf struct { EphemeralStorage string `default:"unlimited"` } `prefix:"REQUEST_"` } `prefix:"RESOURCE_"` + TerminationGracePeriodSeconds int64 `default:"120"` } `prefix:"VM_VMALERTMANAGER_"` DisableSelfServiceScrapeCreation bool `default:"false" env:"VM_DISABLESELFSERVICESCRAPECREATION"` @@ -394,6 +412,7 @@ type BaseOperatorConf struct { EphemeralStorage string `default:"unlimited"` } `prefix:"REQUEST_"` } `prefix:"RESOURCE_"` + TerminationGracePeriodSeconds int64 `default:"30"` } `prefix:"VM_VMBACKUP_"` VMAuth struct { Image string `default:"victoriametrics/vmauth"` @@ -412,15 +431,17 @@ type BaseOperatorConf struct { EphemeralStorage string `default:"unlimited"` } `prefix:"REQUEST_"` } `prefix:"RESOURCE_"` + TerminationGracePeriodSeconds int64 `default:"30"` } `prefix:"VM_VMAUTHDEFAULT_"` VLCluster struct { UseDefaultResources bool `default:"true" env:"USEDEFAULTRESOURCES"` Select struct { - Image string `default:"victoriametrics/victoria-logs"` - Version string `env:",expand" default:"${VM_LOGS_VERSION}"` - Port string `default:"9471"` - Resource struct { + Image string `default:"victoriametrics/victoria-logs"` + Version string `env:",expand" default:"${VM_LOGS_VERSION}"` + Port string `default:"9471"` + UseDefaultResources bool `env:"USEDEFAULTRESOURCES,expand" default:"${VM_VLCLUSTERDEFAULT_USEDEFAULTRESOURCES}"` + Resource struct { Limit struct { Mem string `default:"1024Mi"` Cpu string `default:"1000m"` @@ -432,12 +453,14 @@ type BaseOperatorConf struct { EphemeralStorage string `default:"unlimited"` } `prefix:"REQUEST_"` } `prefix:"RESOURCE_"` + TerminationGracePeriodSeconds int64 `default:"30"` } `prefix:"VLSELECTDEFAULT_"` Storage struct { - Image string `default:"victoriametrics/victoria-logs"` - Version string `env:",expand" default:"${VM_LOGS_VERSION}"` - Port string `default:"9491"` - Resource struct { + Image string `default:"victoriametrics/victoria-logs"` + Version string `env:",expand" default:"${VM_LOGS_VERSION}"` + Port string `default:"9491"` + UseDefaultResources bool `env:"USEDEFAULTRESOURCES,expand" default:"${VM_VLCLUSTERDEFAULT_USEDEFAULTRESOURCES}"` + Resource struct { Limit struct { Mem string `default:"2048Mi"` Cpu string `default:"1000m"` @@ -449,12 +472,14 @@ type BaseOperatorConf struct { EphemeralStorage string `default:"unlimited"` } `prefix:"REQUEST_"` } `prefix:"RESOURCE_"` + TerminationGracePeriodSeconds int64 `default:"30"` } `prefix:"VLSTORAGEDEFAULT_"` Insert struct { - Image string `default:"victoriametrics/victoria-logs"` - Version string `env:",expand" default:"${VM_LOGS_VERSION}"` - Port string `default:"9481"` - Resource struct { + Image string `default:"victoriametrics/victoria-logs"` + Version string `env:",expand" default:"${VM_LOGS_VERSION}"` + Port string `default:"9481"` + UseDefaultResources bool `env:"USEDEFAULTRESOURCES,expand" default:"${VM_VLCLUSTERDEFAULT_USEDEFAULTRESOURCES}"` + Resource struct { Limit struct { Mem string `default:"1024Mi"` Cpu string `default:"1000m"` @@ -466,16 +491,18 @@ type BaseOperatorConf struct { EphemeralStorage string `default:"unlimited"` } `prefix:"REQUEST_"` } `prefix:"RESOURCE_"` + TerminationGracePeriodSeconds int64 `default:"30"` } `prefix:"VLINSERTDEFAULT_"` } `prefix:"VM_VLCLUSTERDEFAULT_"` VTCluster struct { UseDefaultResources bool `default:"true" env:"USEDEFAULTRESOURCES"` Select struct { - Image string `default:"victoriametrics/victoria-traces"` - Version string `env:",expand" default:"${VM_TRACES_VERSION}"` - Port string `default:"10471"` - Resource struct { + Image string `default:"victoriametrics/victoria-traces"` + Version string `env:",expand" default:"${VM_TRACES_VERSION}"` + Port string `default:"10471"` + UseDefaultResources bool `env:"USEDEFAULTRESOURCES,expand" default:"${VM_VTCLUSTERDEFAULT_USEDEFAULTRESOURCES}"` + Resource struct { Limit struct { Mem string `default:"1024Mi"` Cpu string `default:"1000m"` @@ -487,12 +514,14 @@ type BaseOperatorConf struct { EphemeralStorage string `default:"unlimited"` } `prefix:"REQUEST_"` } `prefix:"RESOURCE_"` + TerminationGracePeriodSeconds int64 `default:"30"` } `prefix:"SELECT_"` Storage struct { - Image string `default:"victoriametrics/victoria-traces"` - Version string `env:",expand" default:"${VM_TRACES_VERSION}"` - Port string `default:"10491"` - Resource struct { + Image string `default:"victoriametrics/victoria-traces"` + Version string `env:",expand" default:"${VM_TRACES_VERSION}"` + Port string `default:"10491"` + UseDefaultResources bool `env:"USEDEFAULTRESOURCES,expand" default:"${VM_VTCLUSTERDEFAULT_USEDEFAULTRESOURCES}"` + Resource struct { Limit struct { Mem string `default:"2048Mi"` Cpu string `default:"1000m"` @@ -504,12 +533,14 @@ type BaseOperatorConf struct { EphemeralStorage string `default:"unlimited"` } `prefix:"REQUEST_"` } `prefix:"RESOURCE_"` + TerminationGracePeriodSeconds int64 `default:"30"` } `prefix:"STORAGE_"` Insert struct { - Image string `default:"victoriametrics/victoria-traces"` - Version string `env:",expand" default:"${VM_TRACES_VERSION}"` - Port string `default:"10481"` - Resource struct { + Image string `default:"victoriametrics/victoria-traces"` + Version string `env:",expand" default:"${VM_TRACES_VERSION}"` + Port string `default:"10481"` + UseDefaultResources bool `env:"USEDEFAULTRESOURCES,expand" default:"${VM_VTCLUSTERDEFAULT_USEDEFAULTRESOURCES}"` + Resource struct { Limit struct { Mem string `default:"1024Mi"` Cpu string `default:"1000m"` @@ -521,6 +552,7 @@ type BaseOperatorConf struct { EphemeralStorage string `default:"unlimited"` } `prefix:"REQUEST_"` } `prefix:"RESOURCE_"` + TerminationGracePeriodSeconds int64 `default:"30"` } `prefix:"INSERT_"` } `prefix:"VM_VTCLUSTERDEFAULT_"` @@ -657,7 +689,7 @@ func (boc BaseOperatorConf) validate() error { if err := validateResource("vminsert", Resource(boc.VMCluster.Insert.Resource)); err != nil { return err } - if err := validateResource("vmstorage", Resource(boc.VMCluster.Storage.Resource)); err != nil { + if err := validateResource("vmstorage", Resource(boc.VMCluster.Storage.Common.Resource)); err != nil { return err } if err := validateResource("vmsingle", Resource(boc.VMSingle.Resource)); err != nil { @@ -778,8 +810,14 @@ func (labels *Labels) Set(value string) error { // ConfigAsMetrics exposes major configuration params as prometheus metrics func ConfigAsMetrics(r metrics.RegistererGatherer, cfg *BaseOperatorConf) { opts := getEnvOpts() - mapper := func(v string) string { - return opts.Environment[v] + rawEnvVars := make(map[string]string) + var mapper func(v string) string + mapper = func(v string) string { + val := rawEnvVars[v] + if val == "" { + val = opts.Environment[v] + } + return os.Expand(val, mapper) } params, err := env.GetFieldParamsWithOptions(cfg, opts) if err != nil { @@ -794,6 +832,7 @@ func ConfigAsMetrics(r metrics.RegistererGatherer, cfg *BaseOperatorConf) { } else if p.Expand { value = os.Expand(value, mapper) } + rawEnvVars[p.Key] = value ms.WithLabelValues(p.Key, strconv.FormatBool(isSet), value).Set(1) } r.MustRegister(ms) diff --git a/internal/controller/operator/factory/build/build_test.go b/internal/controller/operator/factory/build/build_test.go index 884a3cb20..35ba0ef67 100644 --- a/internal/controller/operator/factory/build/build_test.go +++ b/internal/controller/operator/factory/build/build_test.go @@ -144,13 +144,13 @@ func TestDeepMerge(t *testing.T) { ServiceAccountName: "base", RetentionPeriod: "30d", VMSelect: &vmv1beta1.VMSelect{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), ExtraArgs: map[string]string{"keep": "x", "override": "old"}, }, }, VMInsert: &vmv1beta1.VMInsert{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), ExtraArgs: map[string]string{"insert-arg": "1"}, }, @@ -167,7 +167,7 @@ func TestDeepMerge(t *testing.T) { override: &vmv1beta1.VMClusterSpec{ ClusterVersion: "v1.2.3", VMSelect: &vmv1beta1.VMSelect{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(3)), ExtraArgs: map[string]string{"override": "new", "add": "y"}, }, diff --git a/internal/controller/operator/factory/build/container.go b/internal/controller/operator/factory/build/container.go index 18b65e0a7..2f4afb8bf 100644 --- a/internal/controller/operator/factory/build/container.go +++ b/internal/controller/operator/factory/build/container.go @@ -19,95 +19,71 @@ const probeTimeoutSeconds int32 = 5 const DataVolumeName = "data" type probeCRD interface { - Probe() *vmv1beta1.EmbeddedProbes ProbePath() string ProbeScheme() string ProbePort() string ProbeNeedLiveness() bool + UseProxyProtocol() bool } -// Probe builds probe for container with possible custom values with -func Probe(container corev1.Container, cr probeCRD) corev1.Container { - // ep *vmv1beta1.EmbeddedProbes, probePath func() string, port string, needAddLiveness bool) corev1.Container { - var rp, lp, sp *corev1.Probe - ep := cr.Probe() - probePath := cr.ProbePath - port := cr.ProbePort() - needAddLiveness := cr.ProbeNeedLiveness() +// Probe adds probe for container +func Probe(container *corev1.Container, cr probeCRD, params *vmv1beta1.CommonAppsParams) { + port := intstr.Parse(cr.ProbePort()) scheme := cr.ProbeScheme() - if ep != nil { - rp = ep.ReadinessProbe - lp = ep.LivenessProbe - sp = ep.StartupProbe - } - - defaultProbeHandler := func() corev1.ProbeHandler { - return corev1.ProbeHandler{ - HTTPGet: &corev1.HTTPGetAction{ - Port: intstr.Parse(port), - Scheme: corev1.URIScheme(scheme), - Path: probePath(), - }, - } - } - if rp == nil { - rp = &corev1.Probe{ - ProbeHandler: defaultProbeHandler(), - TimeoutSeconds: probeTimeoutSeconds, - PeriodSeconds: 5, - FailureThreshold: 10, - } - } - if needAddLiveness { - if lp == nil { - lp = &corev1.Probe{ - ProbeHandler: defaultProbeHandler(), - TimeoutSeconds: probeTimeoutSeconds, - FailureThreshold: 10, - PeriodSeconds: 5, + path := cr.ProbePath() + getProbe := func(probe *corev1.Probe, createIfNil bool) *corev1.Probe { + if probe == nil { + if createIfNil { + probe = new(corev1.Probe) + } else { + return probe } } - } - // ensure, that custom probe has all needed fields. - addMissingFields := func(probe *corev1.Probe) { - if probe != nil { - - if probe.HTTPGet == nil && probe.TCPSocket == nil && probe.Exec == nil { - probe.HTTPGet = &corev1.HTTPGetAction{ - Port: intstr.Parse(port), - Scheme: corev1.URIScheme(scheme), - Path: probePath(), - } + if probe.HTTPGet == nil && probe.TCPSocket == nil && probe.Exec == nil { + if cr.UseProxyProtocol() { + probe.TCPSocket = new(corev1.TCPSocketAction) + } else { + probe.HTTPGet = new(corev1.HTTPGetAction) } - if probe.HTTPGet != nil { - if probe.HTTPGet.Path == "" { - probe.HTTPGet.Path = probePath() - } - if probe.HTTPGet.Port.StrVal == "" && probe.HTTPGet.Port.IntVal == 0 { - probe.HTTPGet.Port = intstr.Parse(port) - } - } - if probe.PeriodSeconds == 0 { - probe.PeriodSeconds = 5 + } + if cr.UseProxyProtocol() && probe.TCPSocket != nil { + if probe.TCPSocket.Port.StrVal == "" && probe.TCPSocket.Port.IntVal == 0 { + probe.TCPSocket.Port = port } - if probe.FailureThreshold == 0 { - probe.FailureThreshold = 10 + } else if probe.HTTPGet != nil { + if len(probe.HTTPGet.Path) == 0 { + probe.HTTPGet.Path = path } - if probe.TimeoutSeconds == 0 { - probe.TimeoutSeconds = probeTimeoutSeconds + if probe.HTTPGet.Port.StrVal == "" && probe.HTTPGet.Port.IntVal == 0 { + probe.HTTPGet.Port = port } - if probe.SuccessThreshold == 0 { - probe.SuccessThreshold = 1 + if len(probe.HTTPGet.Scheme) == 0 { + probe.HTTPGet.Scheme = corev1.URIScheme(scheme) } } + if probe.PeriodSeconds == 0 { + probe.PeriodSeconds = 5 + } + if probe.FailureThreshold == 0 { + probe.FailureThreshold = 10 + } + if probe.TimeoutSeconds == 0 { + probe.TimeoutSeconds = probeTimeoutSeconds + } + if probe.SuccessThreshold == 0 { + probe.SuccessThreshold = 1 + } + return probe + } + var liveness, readiness, startup *corev1.Probe + if params != nil { + liveness, readiness, startup = params.LivenessProbe, params.ReadinessProbe, params.StartupProbe + } + if cr.ProbeNeedLiveness() { + container.LivenessProbe = getProbe(liveness, true) } - addMissingFields(lp) - addMissingFields(sp) - addMissingFields(rp) - container.LivenessProbe = lp - container.StartupProbe = sp - container.ReadinessProbe = rp - return container + container.StartupProbe = getProbe(startup, false) + container.ReadinessProbe = getProbe(readiness, true) } // Resources creates container resources with conditional defaults values diff --git a/internal/controller/operator/factory/build/container_test.go b/internal/controller/operator/factory/build/container_test.go index e2e763d8d..630cdabe0 100644 --- a/internal/controller/operator/factory/build/container_test.go +++ b/internal/controller/operator/factory/build/container_test.go @@ -17,19 +17,15 @@ import ( ) type testBuildProbeCR struct { - ep *vmv1beta1.EmbeddedProbes - probePath func() string - port string - scheme string - needAddLiveness bool -} - -func (t testBuildProbeCR) Probe() *vmv1beta1.EmbeddedProbes { - return t.ep + probePath string + port string + scheme string + useProxyProtocol bool + needAddLiveness bool } func (t testBuildProbeCR) ProbePath() string { - return t.probePath() + return t.probePath } func (t testBuildProbeCR) ProbeScheme() string { @@ -44,24 +40,27 @@ func (t testBuildProbeCR) ProbeNeedLiveness() bool { return t.needAddLiveness } +func (t testBuildProbeCR) UseProxyProtocol() bool { + return t.useProxyProtocol +} + func Test_buildProbe(t *testing.T) { type opts struct { container corev1.Container cr testBuildProbeCR validate func(corev1.Container) + params vmv1beta1.CommonAppsParams } f := func(o opts) { t.Helper() - got := Probe(o.container, o.cr) - o.validate(got) + Probe(&o.container, o.cr, &o.params) + o.validate(o.container) } // build default probe with empty ep f(opts{ cr: testBuildProbeCR{ - probePath: func() string { - return "/health" - }, + probePath: "/health", port: "8051", needAddLiveness: true, scheme: "HTTP", @@ -77,9 +76,7 @@ func Test_buildProbe(t *testing.T) { // build default probe with empty ep using HTTPS f(opts{ cr: testBuildProbeCR{ - probePath: func() string { - return "/health" - }, + probePath: "/health", port: "8051", needAddLiveness: true, scheme: "HTTPS", @@ -96,35 +93,33 @@ func Test_buildProbe(t *testing.T) { // build default probe with ep f(opts{ cr: testBuildProbeCR{ - probePath: func() string { - return "/health" - }, + probePath: "/health", port: "8051", needAddLiveness: true, - ep: &vmv1beta1.EmbeddedProbes{ - ReadinessProbe: &corev1.Probe{ - ProbeHandler: corev1.ProbeHandler{ - Exec: &corev1.ExecAction{ - Command: []string{"echo", "1"}, - }, + }, + params: vmv1beta1.CommonAppsParams{ + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + Exec: &corev1.ExecAction{ + Command: []string{"echo", "1"}, }, }, - StartupProbe: &corev1.Probe{ - ProbeHandler: corev1.ProbeHandler{ - HTTPGet: &corev1.HTTPGetAction{ - Host: "some", - }, + }, + StartupProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Host: "some", }, }, - LivenessProbe: &corev1.Probe{ - ProbeHandler: corev1.ProbeHandler{ - HTTPGet: &corev1.HTTPGetAction{ - Path: "/live1", - }, + }, + LivenessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/live1", }, - TimeoutSeconds: 15, - InitialDelaySeconds: 20, }, + TimeoutSeconds: 15, + InitialDelaySeconds: 20, }, }, container: corev1.Container{}, @@ -592,7 +587,7 @@ func TestBuildConfigReloaderContainer(t *testing.T) { Name: "base", }, Spec: vmv1beta1.VMAlertSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ExtraArgs: map[string]string{ "reloadAuthKey": "test", }, @@ -726,7 +721,7 @@ func TestBuildConfigReloaderContainer(t *testing.T) { Name: "base", }, Spec: vmv1beta1.VMAlertSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ConfigMaps: []string{"extra-template-1", "extra-template-2"}, }, }, diff --git a/internal/controller/operator/factory/build/daemonset.go b/internal/controller/operator/factory/build/daemonset.go index c82f22dd0..df1d0d425 100644 --- a/internal/controller/operator/factory/build/daemonset.go +++ b/internal/controller/operator/factory/build/daemonset.go @@ -8,7 +8,7 @@ import ( ) // DeploymentAddCommonParams adds common params for all deployments -func DaemonSetAddCommonParams(dst *appsv1.DaemonSet, useStrictSecurity bool, params *vmv1beta1.CommonApplicationDeploymentParams) { +func DaemonSetAddCommonParams(dst *appsv1.DaemonSet, params *vmv1beta1.CommonAppsParams) { dst.Spec.Template.Spec.Affinity = params.Affinity dst.Spec.Template.Spec.Tolerations = params.Tolerations dst.Spec.Template.Spec.SchedulerName = params.SchedulerName @@ -22,11 +22,10 @@ func DaemonSetAddCommonParams(dst *appsv1.DaemonSet, useStrictSecurity bool, par dst.Spec.Template.Spec.DNSPolicy = params.DNSPolicy dst.Spec.Template.Spec.DNSConfig = params.DNSConfig dst.Spec.Template.Spec.NodeSelector = params.NodeSelector - dst.Spec.Template.Spec.SecurityContext = AddStrictSecuritySettingsWithRootToPod(params.SecurityContext, useStrictSecurity) + dst.Spec.Template.Spec.SecurityContext = addStrictSecuritySettingsWithRootToPod(params) dst.Spec.Template.Spec.TerminationGracePeriodSeconds = params.TerminationGracePeriodSeconds dst.Spec.Template.Spec.TopologySpreadConstraints = params.TopologySpreadConstraints dst.Spec.Template.Spec.ImagePullSecrets = params.ImagePullSecrets - dst.Spec.Template.Spec.TerminationGracePeriodSeconds = params.TerminationGracePeriodSeconds dst.Spec.Template.Spec.ReadinessGates = params.ReadinessGates dst.Spec.MinReadySeconds = params.MinReadySeconds dst.Spec.RevisionHistoryLimit = params.RevisionHistoryLimitCount diff --git a/internal/controller/operator/factory/build/defaults.go b/internal/controller/operator/factory/build/defaults.go index 8a04d6471..834f46bee 100644 --- a/internal/controller/operator/factory/build/defaults.go +++ b/internal/controller/operator/factory/build/defaults.go @@ -17,6 +17,13 @@ import ( "github.com/VictoriaMetrics/operator/internal/config" ) +type commonParams struct { + tag string + useStrictSecurity *bool + license *vmv1beta1.License + imagePullSecrets []corev1.LocalObjectReference +} + func getCfg() *config.BaseOperatorConf { return config.MustGetBaseConfig() } @@ -219,7 +226,10 @@ func addVMAuthDefaults(objI any) { } } cv := config.ApplicationDefaults(c.VMAuth) - addDefaultsToCommonParams(&cr.Spec.CommonDefaultableParams, cr.Spec.License, &cv) + cp := commonParams{ + license: cr.Spec.License, + } + addDefaultsToCommonParams(&cr.Spec.CommonAppsParams, &cp, &cv) addDefaultsToConfigReloader(&cr.Spec.CommonConfigReloaderParams, ptr.Deref(cr.Spec.UseDefaultResources, false)) } @@ -228,7 +238,10 @@ func addVMAlertDefaults(objI any) { c := getCfg() cv := config.ApplicationDefaults(c.VMAlert) - addDefaultsToCommonParams(&cr.Spec.CommonDefaultableParams, cr.Spec.License, &cv) + cp := commonParams{ + license: cr.Spec.License, + } + addDefaultsToCommonParams(&cr.Spec.CommonAppsParams, &cp, &cv) addDefaultsToConfigReloader(&cr.Spec.CommonConfigReloaderParams, ptr.Deref(cr.Spec.UseDefaultResources, false)) if cr.Spec.ConfigReloaderImage == "" { panic("cannot be empty") @@ -240,7 +253,10 @@ func addVMAgentDefaults(objI any) { c := getCfg() cv := config.ApplicationDefaults(c.VMAgent) - addDefaultsToCommonParams(&cr.Spec.CommonDefaultableParams, cr.Spec.License, &cv) + cp := commonParams{ + license: cr.Spec.License, + } + addDefaultsToCommonParams(&cr.Spec.CommonAppsParams, &cp, &cv) addDefaultsToConfigReloader(&cr.Spec.CommonConfigReloaderParams, ptr.Deref(cr.Spec.UseDefaultResources, false)) if cr.Spec.IngestOnlyMode == nil { cr.Spec.IngestOnlyMode = ptr.To(false) @@ -252,19 +268,25 @@ func addVLAgentDefaults(objI any) { c := getCfg() cv := config.ApplicationDefaults(c.VLAgent) - addDefaultsToCommonParams(&cr.Spec.CommonDefaultableParams, cr.Spec.License, &cv) + cp := commonParams{ + license: cr.Spec.License, + } + addDefaultsToCommonParams(&cr.Spec.CommonAppsParams, &cp, &cv) } func addVMSingleDefaults(objI any) { cr := objI.(*vmv1beta1.VMSingle) c := getCfg() - useBackupDefaultResources := c.VMBackup.UseDefaultResources cv := config.ApplicationDefaults(c.VMSingle) - addDefaultsToCommonParams(&cr.Spec.CommonDefaultableParams, cr.Spec.License, &cv) + cp := commonParams{ + license: cr.Spec.License, + } + addDefaultsToCommonParams(&cr.Spec.CommonAppsParams, &cp, &cv) + bv := config.ApplicationDefaults(c.VMBackup) + useBackupDefaultResources := c.VMBackup.UseDefaultResources if cr.Spec.UseDefaultResources != nil { useBackupDefaultResources = *cr.Spec.UseDefaultResources } - bv := config.ApplicationDefaults(c.VMBackup) addDefaultsToVMBackup(cr.Spec.VMBackup, useBackupDefaultResources, &bv) } @@ -272,30 +294,33 @@ func addVLogsDefaults(objI any) { cr := objI.(*vmv1beta1.VLogs) c := getCfg() cv := config.ApplicationDefaults(c.VLogs) - addDefaultsToCommonParams(&cr.Spec.CommonDefaultableParams, nil, &cv) + addDefaultsToCommonParams(&cr.Spec.CommonAppsParams, nil, &cv) } func addVMAnomalyDefaults(objI any) { cr := objI.(*vmv1.VMAnomaly) // vmanomaly takes up to 2 minutes to start - if cr.Spec.EmbeddedProbes == nil { - cr.Spec.EmbeddedProbes = &vmv1beta1.EmbeddedProbes{ - LivenessProbe: &corev1.Probe{ - InitialDelaySeconds: 10, - FailureThreshold: 16, - PeriodSeconds: 10, - }, - ReadinessProbe: &corev1.Probe{ - InitialDelaySeconds: 10, - FailureThreshold: 16, - PeriodSeconds: 10, - }, + if cr.Spec.LivenessProbe == nil { + cr.Spec.LivenessProbe = &corev1.Probe{ + InitialDelaySeconds: 10, + FailureThreshold: 16, + PeriodSeconds: 10, + } + } + if cr.Spec.ReadinessProbe == nil { + cr.Spec.ReadinessProbe = &corev1.Probe{ + InitialDelaySeconds: 10, + FailureThreshold: 16, + PeriodSeconds: 10, } } c := getCfg() cv := config.ApplicationDefaults(c.VMAnomaly) - addDefaultsToCommonParams(&cr.Spec.CommonDefaultableParams, nil, &cv) + cp := commonParams{ + license: cr.Spec.License, + } + addDefaultsToCommonParams(&cr.Spec.CommonAppsParams, &cp, &cv) if cr.Spec.Monitoring == nil { cr.Spec.Monitoring = &vmv1.VMAnomalyMonitoringSpec{ Pull: &vmv1.VMAnomalyMonitoringPullSpec{ @@ -309,14 +334,17 @@ func addVLSingleDefaults(objI any) { cr := objI.(*vmv1.VLSingle) c := getCfg() cv := config.ApplicationDefaults(c.VLSingle) - addDefaultsToCommonParams(&cr.Spec.CommonDefaultableParams, cr.Spec.License, &cv) + cp := commonParams{ + license: cr.Spec.License, + } + addDefaultsToCommonParams(&cr.Spec.CommonAppsParams, &cp, &cv) } func addVTSingleDefaults(objI any) { cr := objI.(*vmv1.VTSingle) c := getCfg() cv := config.ApplicationDefaults(c.VTSingle) - addDefaultsToCommonParams(&cr.Spec.CommonDefaultableParams, nil, &cv) + addDefaultsToCommonParams(&cr.Spec.CommonAppsParams, nil, &cv) } func addVMAlertmanagerDefaults(objI any) { @@ -332,10 +360,7 @@ func addVMAlertmanagerDefaults(objI any) { if cr.Spec.PortName == "" { cr.Spec.PortName = "web" } - if cr.Spec.TerminationGracePeriodSeconds == nil { - cr.Spec.TerminationGracePeriodSeconds = ptr.To[int64](120) - } - addDefaultsToCommonParams(&cr.Spec.CommonDefaultableParams, nil, &cv) + addDefaultsToCommonParams(&cr.Spec.CommonAppsParams, nil, &cv) addDefaultsToConfigReloader(&cr.Spec.CommonConfigReloaderParams, ptr.Deref(cr.Spec.UseDefaultResources, false)) } @@ -343,178 +368,10 @@ const ( vmStorageDefaultDBPath = "vmstorage-data" ) -var defaultTerminationGracePeriod = int64(30) - -func addVMClusterSpecDefaults(spec *vmv1beta1.VMClusterSpec) { - c := getCfg() - - // cluster is tricky is has main strictSecurity and per app - useStrictSecurity := c.EnableStrictSecurity - if spec.UseStrictSecurity != nil { - useStrictSecurity = *spec.UseStrictSecurity - } - if spec.ClusterDomainName == "" { - spec.ClusterDomainName = c.ClusterDomainName - } - - if spec.VMStorage != nil { - if spec.VMStorage.UseStrictSecurity == nil { - spec.VMStorage.UseStrictSecurity = &useStrictSecurity - } - if spec.VMStorage.DisableSelfServiceScrape == nil { - spec.VMStorage.DisableSelfServiceScrape = &c.DisableSelfServiceScrapeCreation - } - spec.VMStorage.ImagePullSecrets = append(spec.VMStorage.ImagePullSecrets, spec.ImagePullSecrets...) - - useBackupDefaultResources := c.VMBackup.UseDefaultResources - if spec.VMStorage.UseDefaultResources != nil { - useBackupDefaultResources = *spec.VMStorage.UseDefaultResources - } - bv := config.ApplicationDefaults(c.VMBackup) - if spec.VMStorage.Image.Repository == "" { - spec.VMStorage.Image.Repository = c.VMCluster.Storage.Image - } - spec.VMStorage.Image.Repository = formatContainerImage(c.ContainerRegistry, spec.VMStorage.Image.Repository) - - if spec.VMStorage.Image.Tag == "" { - if spec.ClusterVersion != "" { - spec.VMStorage.Image.Tag = spec.ClusterVersion - } else { - spec.VMStorage.Image.Tag = c.VMCluster.Storage.Version - } - } - if spec.VMStorage.VMInsertPort == "" { - spec.VMStorage.VMInsertPort = c.VMCluster.Storage.VMInsertPort - } - if spec.VMStorage.VMSelectPort == "" { - spec.VMStorage.VMSelectPort = c.VMCluster.Storage.VMSelectPort - } - if spec.VMStorage.Port == "" { - spec.VMStorage.Port = c.VMCluster.Storage.Port - } - - if spec.VMStorage.DNSPolicy == "" { - spec.VMStorage.DNSPolicy = corev1.DNSClusterFirst - } - if spec.VMStorage.SchedulerName == "" { - spec.VMStorage.SchedulerName = "default-scheduler" - } - if spec.VMStorage.Image.PullPolicy == "" { - spec.VMStorage.Image.PullPolicy = corev1.PullIfNotPresent - } - if spec.VMStorage.StorageDataPath == "" { - spec.VMStorage.StorageDataPath = vmStorageDefaultDBPath - } - if spec.VMStorage.TerminationGracePeriodSeconds == nil { - spec.VMStorage.TerminationGracePeriodSeconds = &defaultTerminationGracePeriod - } - if spec.VMStorage.UseDefaultResources == nil { - spec.VMStorage.UseDefaultResources = &c.VMCluster.UseDefaultResources - } - spec.VMStorage.Resources = Resources(spec.VMStorage.Resources, - config.Resource(c.VMCluster.Storage.Resource), - *spec.VMStorage.UseDefaultResources, - ) - addDefaultsToVMBackup(spec.VMStorage.VMBackup, useBackupDefaultResources, &bv) - } - - if spec.VMInsert != nil { - if spec.VMInsert.UseStrictSecurity == nil { - spec.VMInsert.UseStrictSecurity = &useStrictSecurity - } - if spec.VMInsert.DisableSelfServiceScrape == nil { - spec.VMInsert.DisableSelfServiceScrape = &c.DisableSelfServiceScrapeCreation - } - spec.VMInsert.ImagePullSecrets = append(spec.VMInsert.ImagePullSecrets, spec.ImagePullSecrets...) - - if spec.VMInsert.Image.Repository == "" { - spec.VMInsert.Image.Repository = c.VMCluster.Insert.Image - } - spec.VMInsert.Image.Repository = formatContainerImage(c.ContainerRegistry, spec.VMInsert.Image.Repository) - if spec.VMInsert.Image.Tag == "" { - if spec.ClusterVersion != "" { - spec.VMInsert.Image.Tag = spec.ClusterVersion - } else { - spec.VMInsert.Image.Tag = c.VMCluster.Insert.Version - } - } - if spec.VMInsert.Port == "" { - spec.VMInsert.Port = c.VMCluster.Insert.Port - } - if spec.VMInsert.UseDefaultResources == nil { - spec.VMInsert.UseDefaultResources = &c.VMCluster.UseDefaultResources - } - spec.VMInsert.Resources = Resources(spec.VMInsert.Resources, - config.Resource(c.VMCluster.Insert.Resource), - *spec.VMInsert.UseDefaultResources, - ) - - } - if spec.VMSelect != nil { - if spec.VMSelect.UseStrictSecurity == nil { - spec.VMSelect.UseStrictSecurity = &useStrictSecurity - } - if spec.VMSelect.DisableSelfServiceScrape == nil { - spec.VMSelect.DisableSelfServiceScrape = &c.DisableSelfServiceScrapeCreation - } - - spec.VMSelect.ImagePullSecrets = append(spec.VMSelect.ImagePullSecrets, spec.ImagePullSecrets...) - - if spec.VMSelect.Image.Repository == "" { - spec.VMSelect.Image.Repository = c.VMCluster.Select.Image - } - spec.VMSelect.Image.Repository = formatContainerImage(c.ContainerRegistry, spec.VMSelect.Image.Repository) - if spec.VMSelect.Image.Tag == "" { - if spec.ClusterVersion != "" { - spec.VMSelect.Image.Tag = spec.ClusterVersion - } else { - spec.VMSelect.Image.Tag = c.VMCluster.Select.Version - } - } - if spec.VMSelect.Port == "" { - spec.VMSelect.Port = c.VMCluster.Select.Port - } - - if spec.VMSelect.DNSPolicy == "" { - spec.VMSelect.DNSPolicy = corev1.DNSClusterFirst - } - if spec.VMSelect.SchedulerName == "" { - spec.VMSelect.SchedulerName = "default-scheduler" - } - if spec.VMSelect.Image.PullPolicy == "" { - spec.VMSelect.Image.PullPolicy = corev1.PullIfNotPresent - } - // use "/cache" as default cache dir instead of "/tmp" if `CacheMountPath` not set - if spec.VMSelect.CacheMountPath == "" { - spec.VMSelect.CacheMountPath = "/cache" - } - if spec.VMSelect.UseDefaultResources == nil { - spec.VMSelect.UseDefaultResources = &c.VMCluster.UseDefaultResources - } - spec.VMSelect.Resources = Resources(spec.VMSelect.Resources, - config.Resource(c.VMCluster.Select.Resource), - *spec.VMSelect.UseDefaultResources, - ) - } - if spec.RequestsLoadBalancer.Enabled { - addRequestsLoadBalancerDefaults(&spec.RequestsLoadBalancer, useStrictSecurity, spec.License, spec.ImagePullSecrets) - } -} - -func addRequestsLoadBalancerDefaults(lb *vmv1beta1.VMAuthLoadBalancer, useStrictSecurity bool, license *vmv1beta1.License, pullSecrets []corev1.LocalObjectReference) { +func addRequestsLoadBalancerDefaults(lb *vmv1beta1.VMAuthLoadBalancer, cp *commonParams) { c := getCfg() - if lb.Spec.UseStrictSecurity == nil { - lb.Spec.UseStrictSecurity = &useStrictSecurity - } - if lb.Spec.DisableSelfServiceScrape == nil { - lb.Spec.DisableSelfServiceScrape = &c.DisableSelfServiceScrapeCreation - } - lb.Spec.ImagePullSecrets = append(lb.Spec.ImagePullSecrets, pullSecrets...) cv := config.ApplicationDefaults(c.VMAuth) - addDefaultsToCommonParams(&lb.Spec.CommonDefaultableParams, license, &cv) - if lb.Spec.EmbeddedProbes == nil { - lb.Spec.EmbeddedProbes = &vmv1beta1.EmbeddedProbes{} - } + addDefaultsToCommonParams(&lb.Spec.CommonAppsParams, cp, &cv) if lb.Spec.StartupProbe == nil { lb.Spec.StartupProbe = &corev1.Probe{ ProbeHandler: corev1.ProbeHandler{ @@ -529,41 +386,100 @@ func addRequestsLoadBalancerDefaults(lb *vmv1beta1.VMAuthLoadBalancer, useStrict func addVMClusterDefaults(objI any) { cr := objI.(*vmv1beta1.VMCluster) - addVMClusterSpecDefaults(&cr.Spec) + c := getCfg() + if cr.Spec.ClusterDomainName == "" { + cr.Spec.ClusterDomainName = c.ClusterDomainName + } + cp := commonParams{ + useStrictSecurity: cr.Spec.UseStrictSecurity, + tag: cr.Spec.ClusterVersion, + license: cr.Spec.License, + imagePullSecrets: cr.Spec.ImagePullSecrets, + } + if cr.Spec.VMStorage != nil { + if cr.Spec.VMStorage.StorageDataPath == "" { + cr.Spec.VMStorage.StorageDataPath = vmStorageDefaultDBPath + } + if cr.Spec.VMStorage.VMInsertPort == "" { + cr.Spec.VMStorage.VMInsertPort = c.VMCluster.Storage.VMInsertPort + } + if cr.Spec.VMStorage.VMSelectPort == "" { + cr.Spec.VMStorage.VMSelectPort = c.VMCluster.Storage.VMSelectPort + } + cv := config.ApplicationDefaults(c.VMCluster.Storage.Common) + addDefaultsToCommonParams(&cr.Spec.VMStorage.CommonAppsParams, &cp, &cv) + + bv := config.ApplicationDefaults(c.VMBackup) + useBackupDefaultResources := c.VMBackup.UseDefaultResources + if cr.Spec.VMStorage.UseDefaultResources != nil { + useBackupDefaultResources = *cr.Spec.VMStorage.UseDefaultResources + } + addDefaultsToVMBackup(cr.Spec.VMStorage.VMBackup, useBackupDefaultResources, &bv) + } + if cr.Spec.VMInsert != nil { + cv := config.ApplicationDefaults(c.VMCluster.Insert) + addDefaultsToCommonParams(&cr.Spec.VMInsert.CommonAppsParams, &cp, &cv) + } + if cr.Spec.VMSelect != nil { + if cr.Spec.VMSelect.CacheMountPath == "" { + cr.Spec.VMSelect.CacheMountPath = "/cache" + } + cv := config.ApplicationDefaults(c.VMCluster.Select) + addDefaultsToCommonParams(&cr.Spec.VMSelect.CommonAppsParams, &cp, &cv) + } + if cr.Spec.RequestsLoadBalancer.Enabled { + addRequestsLoadBalancerDefaults(&cr.Spec.RequestsLoadBalancer, &cp) + } } -func addDefaultsToCommonParams(common *vmv1beta1.CommonDefaultableParams, license *vmv1beta1.License, appDefaults *config.ApplicationDefaults) { +func addDefaultsToCommonParams(common *vmv1beta1.CommonAppsParams, cp *commonParams, appDefaults *config.ApplicationDefaults) { c := getCfg() - if common.Image.Repository == "" { common.Image.Repository = appDefaults.Image } - if common.DisableSelfServiceScrape == nil { - common.DisableSelfServiceScrape = &c.DisableSelfServiceScrapeCreation - } common.Image.Repository = formatContainerImage(c.ContainerRegistry, common.Image.Repository) + useStrictSecurity := c.EnableStrictSecurity if common.Image.Tag == "" { - common.Image.Tag = appDefaults.Version - if license.IsProvided() { + if cp != nil && len(cp.tag) > 0 { + common.Image.Tag = cp.tag + } else { + common.Image.Tag = appDefaults.Version + } + if cp != nil && cp.license.IsProvided() { common.Image.Tag = addEntSuffixToTag(common.Image.Tag) } } + if cp != nil { + common.ImagePullSecrets = append(common.ImagePullSecrets, cp.imagePullSecrets...) + if cp.useStrictSecurity != nil { + useStrictSecurity = *cp.useStrictSecurity + } + } + if common.DisableSelfServiceScrape == nil { + common.DisableSelfServiceScrape = &c.DisableSelfServiceScrapeCreation + } if common.Port == "" { common.Port = appDefaults.Port } if common.Image.PullPolicy == "" { common.Image.PullPolicy = corev1.PullIfNotPresent } - - if common.UseStrictSecurity == nil && c.EnableStrictSecurity { - common.UseStrictSecurity = &c.EnableStrictSecurity + if common.UseStrictSecurity == nil { + common.UseStrictSecurity = &useStrictSecurity } - if common.UseDefaultResources == nil && appDefaults.UseDefaultResources { common.UseDefaultResources = &appDefaults.UseDefaultResources } - + if common.TerminationGracePeriodSeconds == nil { + common.TerminationGracePeriodSeconds = ptr.To(appDefaults.TerminationGracePeriodSeconds) + } + if common.DNSPolicy == "" { + common.DNSPolicy = corev1.DNSClusterFirst + } + if common.SchedulerName == "" { + common.SchedulerName = "default-scheduler" + } common.Resources = Resources(common.Resources, config.Resource(appDefaults.Resource), ptr.Deref(common.UseDefaultResources, false)) } @@ -625,305 +541,67 @@ const ( func addVTClusterDefaults(objI any) { cr := objI.(*vmv1.VTCluster) c := getCfg() - - // cluster is tricky is has main strictSecurity and per app - useStrictSecurity := c.EnableStrictSecurity - if cr.Spec.UseStrictSecurity != nil { - useStrictSecurity = *cr.Spec.UseStrictSecurity + cp := commonParams{ + useStrictSecurity: cr.Spec.UseStrictSecurity, + tag: cr.Spec.ClusterVersion, + license: nil, + imagePullSecrets: cr.Spec.ImagePullSecrets, } if cr.Spec.ClusterDomainName == "" { cr.Spec.ClusterDomainName = c.ClusterDomainName } - if cr.Spec.Storage != nil { - if cr.Spec.Storage.UseStrictSecurity == nil { - cr.Spec.Storage.UseStrictSecurity = &useStrictSecurity - } - if cr.Spec.Storage.DisableSelfServiceScrape == nil { - cr.Spec.Storage.DisableSelfServiceScrape = &c.DisableSelfServiceScrapeCreation - } - cr.Spec.Storage.ImagePullSecrets = append(cr.Spec.Storage.ImagePullSecrets, cr.Spec.ImagePullSecrets...) - - if cr.Spec.Storage.Image.Repository == "" { - cr.Spec.Storage.Image.Repository = c.VTCluster.Storage.Image - } - cr.Spec.Storage.Image.Repository = formatContainerImage(c.ContainerRegistry, cr.Spec.Storage.Image.Repository) - - if cr.Spec.Storage.Image.Tag == "" { - if cr.Spec.ClusterVersion != "" { - cr.Spec.Storage.Image.Tag = cr.Spec.ClusterVersion - } else { - cr.Spec.Storage.Image.Tag = c.VTCluster.Storage.Version - } - } - - if cr.Spec.Storage.Port == "" { - cr.Spec.Storage.Port = c.VTCluster.Storage.Port - } - - if cr.Spec.Storage.DNSPolicy == "" { - cr.Spec.Storage.DNSPolicy = corev1.DNSClusterFirst - } - if cr.Spec.Storage.SchedulerName == "" { - cr.Spec.Storage.SchedulerName = "default-scheduler" - } - if cr.Spec.Storage.Image.PullPolicy == "" { - cr.Spec.Storage.Image.PullPolicy = corev1.PullIfNotPresent - } if cr.Spec.Storage.StorageDataPath == "" { cr.Spec.Storage.StorageDataPath = vtStorageDefaultDBPath } - if cr.Spec.Storage.UseDefaultResources == nil { - cr.Spec.Storage.UseDefaultResources = &c.VTCluster.UseDefaultResources - } - cr.Spec.Storage.Resources = Resources(cr.Spec.Storage.Resources, - config.Resource(c.VTCluster.Storage.Resource), - *cr.Spec.Storage.UseDefaultResources, - ) + cv := config.ApplicationDefaults(c.VTCluster.Storage) + addDefaultsToCommonParams(&cr.Spec.Storage.CommonAppsParams, &cp, &cv) } if cr.Spec.Insert != nil { - if cr.Spec.Insert.UseStrictSecurity == nil { - cr.Spec.Insert.UseStrictSecurity = &useStrictSecurity - } - if cr.Spec.Insert.DisableSelfServiceScrape == nil { - cr.Spec.Insert.DisableSelfServiceScrape = &c.DisableSelfServiceScrapeCreation - } - cr.Spec.Insert.ImagePullSecrets = append(cr.Spec.Insert.ImagePullSecrets, cr.Spec.ImagePullSecrets...) - - if cr.Spec.Insert.Image.Repository == "" { - cr.Spec.Insert.Image.Repository = c.VTCluster.Insert.Image - } - cr.Spec.Insert.Image.Repository = formatContainerImage(c.ContainerRegistry, cr.Spec.Insert.Image.Repository) - if cr.Spec.Insert.Image.Tag == "" { - if cr.Spec.ClusterVersion != "" { - cr.Spec.Insert.Image.Tag = cr.Spec.ClusterVersion - } else { - cr.Spec.Insert.Image.Tag = c.VTCluster.Insert.Version - } - } - if cr.Spec.Insert.Port == "" { - cr.Spec.Insert.Port = c.VTCluster.Insert.Port - } - if cr.Spec.Insert.UseDefaultResources == nil { - cr.Spec.Insert.UseDefaultResources = &c.VTCluster.UseDefaultResources - } - cr.Spec.Insert.Resources = Resources(cr.Spec.Insert.Resources, - config.Resource(c.VTCluster.Insert.Resource), - *cr.Spec.Insert.UseDefaultResources, - ) - + cv := config.ApplicationDefaults(c.VTCluster.Insert) + addDefaultsToCommonParams(&cr.Spec.Insert.CommonAppsParams, &cp, &cv) } if cr.Spec.Select != nil { - if cr.Spec.Select.UseStrictSecurity == nil { - cr.Spec.Select.UseStrictSecurity = &useStrictSecurity - } - if cr.Spec.Select.DisableSelfServiceScrape == nil { - cr.Spec.Select.DisableSelfServiceScrape = &c.DisableSelfServiceScrapeCreation - } - - cr.Spec.Select.ImagePullSecrets = append(cr.Spec.Select.ImagePullSecrets, cr.Spec.ImagePullSecrets...) - - if cr.Spec.Select.Image.Repository == "" { - cr.Spec.Select.Image.Repository = c.VTCluster.Select.Image - } - cr.Spec.Select.Image.Repository = formatContainerImage(c.ContainerRegistry, cr.Spec.Select.Image.Repository) - if cr.Spec.Select.Image.Tag == "" { - if cr.Spec.ClusterVersion != "" { - cr.Spec.Select.Image.Tag = cr.Spec.ClusterVersion - } else { - cr.Spec.Select.Image.Tag = c.VTCluster.Select.Version - } - } - if cr.Spec.Select.Port == "" { - cr.Spec.Select.Port = c.VTCluster.Select.Port - } - - if cr.Spec.Select.DNSPolicy == "" { - cr.Spec.Select.DNSPolicy = corev1.DNSClusterFirst - } - if cr.Spec.Select.SchedulerName == "" { - cr.Spec.Select.SchedulerName = "default-scheduler" - } - if cr.Spec.Select.Image.PullPolicy == "" { - cr.Spec.Select.Image.PullPolicy = corev1.PullIfNotPresent - } - - if cr.Spec.Select.UseDefaultResources == nil { - cr.Spec.Select.UseDefaultResources = &c.VTCluster.UseDefaultResources - } - cr.Spec.Select.Resources = Resources(cr.Spec.Select.Resources, - config.Resource(c.VTCluster.Select.Resource), - *cr.Spec.Select.UseDefaultResources, - ) + cv := config.ApplicationDefaults(c.VTCluster.Select) + addDefaultsToCommonParams(&cr.Spec.Select.CommonAppsParams, &cp, &cv) } if cr.Spec.RequestsLoadBalancer.Enabled { - addRequestsLoadBalancerDefaults(&cr.Spec.RequestsLoadBalancer, useStrictSecurity, nil, cr.Spec.ImagePullSecrets) + addRequestsLoadBalancerDefaults(&cr.Spec.RequestsLoadBalancer, &cp) } } func addVLClusterDefaults(objI any) { cr := objI.(*vmv1.VLCluster) c := getCfg() - - // cluster is tricky is has main strictSecurity and per app - useStrictSecurity := c.EnableStrictSecurity - if cr.Spec.UseStrictSecurity != nil { - useStrictSecurity = *cr.Spec.UseStrictSecurity + cp := commonParams{ + useStrictSecurity: cr.Spec.UseStrictSecurity, + tag: cr.Spec.ClusterVersion, + license: cr.Spec.License, + imagePullSecrets: cr.Spec.ImagePullSecrets, } if cr.Spec.ClusterDomainName == "" { cr.Spec.ClusterDomainName = c.ClusterDomainName } - if cr.Spec.VLStorage != nil { - if cr.Spec.VLStorage.UseStrictSecurity == nil { - cr.Spec.VLStorage.UseStrictSecurity = &useStrictSecurity - } - if cr.Spec.VLStorage.DisableSelfServiceScrape == nil { - cr.Spec.VLStorage.DisableSelfServiceScrape = &c.DisableSelfServiceScrapeCreation - } - cr.Spec.VLStorage.ImagePullSecrets = append(cr.Spec.VLStorage.ImagePullSecrets, cr.Spec.ImagePullSecrets...) - - if cr.Spec.VLStorage.Image.Repository == "" { - cr.Spec.VLStorage.Image.Repository = c.VLCluster.Storage.Image - } - cr.Spec.VLStorage.Image.Repository = formatContainerImage(c.ContainerRegistry, cr.Spec.VLStorage.Image.Repository) - - if cr.Spec.VLStorage.Image.Tag == "" { - if cr.Spec.ClusterVersion != "" { - cr.Spec.VLStorage.Image.Tag = cr.Spec.ClusterVersion - } else { - cr.Spec.VLStorage.Image.Tag = c.VLCluster.Storage.Version - } - } - - if cr.Spec.VLStorage.Port == "" { - cr.Spec.VLStorage.Port = c.VLCluster.Storage.Port - } - - if cr.Spec.VLStorage.DNSPolicy == "" { - cr.Spec.VLStorage.DNSPolicy = corev1.DNSClusterFirst - } - if cr.Spec.VLStorage.SchedulerName == "" { - cr.Spec.VLStorage.SchedulerName = "default-scheduler" - } - if cr.Spec.VLStorage.Image.PullPolicy == "" { - cr.Spec.VLStorage.Image.PullPolicy = corev1.PullIfNotPresent - } if cr.Spec.VLStorage.StorageDataPath == "" { cr.Spec.VLStorage.StorageDataPath = vlStorageDefaultDBPath } - if cr.Spec.VLStorage.UseDefaultResources == nil { - cr.Spec.VLStorage.UseDefaultResources = &c.VLCluster.UseDefaultResources - } - cr.Spec.VLStorage.Resources = Resources(cr.Spec.VLStorage.Resources, - config.Resource(c.VLCluster.Storage.Resource), - *cr.Spec.VLStorage.UseDefaultResources, - ) + cv := config.ApplicationDefaults(c.VLCluster.Storage) + addDefaultsToCommonParams(&cr.Spec.VLStorage.CommonAppsParams, &cp, &cv) } - if cr.Spec.VLInsert != nil { - if cr.Spec.VLInsert.UseStrictSecurity == nil { - cr.Spec.VLInsert.UseStrictSecurity = &useStrictSecurity - } - if cr.Spec.VLInsert.DisableSelfServiceScrape == nil { - cr.Spec.VLInsert.DisableSelfServiceScrape = &c.DisableSelfServiceScrapeCreation - } - cr.Spec.VLInsert.ImagePullSecrets = append(cr.Spec.VLInsert.ImagePullSecrets, cr.Spec.ImagePullSecrets...) - - if cr.Spec.VLInsert.Image.Repository == "" { - cr.Spec.VLInsert.Image.Repository = c.VLCluster.Insert.Image - } - cr.Spec.VLInsert.Image.Repository = formatContainerImage(c.ContainerRegistry, cr.Spec.VLInsert.Image.Repository) - if cr.Spec.VLInsert.Image.Tag == "" { - if cr.Spec.ClusterVersion != "" { - cr.Spec.VLInsert.Image.Tag = cr.Spec.ClusterVersion - } else { - cr.Spec.VLInsert.Image.Tag = c.VLCluster.Insert.Version - } - } - if cr.Spec.VLInsert.Port == "" { - cr.Spec.VLInsert.Port = c.VLCluster.Insert.Port - } - if cr.Spec.VLInsert.UseDefaultResources == nil { - cr.Spec.VLInsert.UseDefaultResources = &c.VLCluster.UseDefaultResources - } - cr.Spec.VLInsert.Resources = Resources(cr.Spec.VLInsert.Resources, - config.Resource(c.VLCluster.Insert.Resource), - *cr.Spec.VLInsert.UseDefaultResources, - ) - + cv := config.ApplicationDefaults(c.VLCluster.Insert) + addDefaultsToCommonParams(&cr.Spec.VLInsert.CommonAppsParams, &cp, &cv) } if cr.Spec.VLSelect != nil { - if cr.Spec.VLSelect.UseStrictSecurity == nil { - cr.Spec.VLSelect.UseStrictSecurity = &useStrictSecurity - } - if cr.Spec.VLSelect.DisableSelfServiceScrape == nil { - cr.Spec.VLSelect.DisableSelfServiceScrape = &c.DisableSelfServiceScrapeCreation - } - - cr.Spec.VLSelect.ImagePullSecrets = append(cr.Spec.VLSelect.ImagePullSecrets, cr.Spec.ImagePullSecrets...) - - if cr.Spec.VLSelect.Image.Repository == "" { - cr.Spec.VLSelect.Image.Repository = c.VLCluster.Select.Image - } - cr.Spec.VLSelect.Image.Repository = formatContainerImage(c.ContainerRegistry, cr.Spec.VLSelect.Image.Repository) - if cr.Spec.VLSelect.Image.Tag == "" { - if cr.Spec.ClusterVersion != "" { - cr.Spec.VLSelect.Image.Tag = cr.Spec.ClusterVersion - } else { - cr.Spec.VLSelect.Image.Tag = c.VLCluster.Select.Version - } - } - if cr.Spec.VLSelect.Port == "" { - cr.Spec.VLSelect.Port = c.VLCluster.Select.Port - } - - if cr.Spec.VLSelect.DNSPolicy == "" { - cr.Spec.VLSelect.DNSPolicy = corev1.DNSClusterFirst - } - if cr.Spec.VLSelect.SchedulerName == "" { - cr.Spec.VLSelect.SchedulerName = "default-scheduler" - } - if cr.Spec.VLSelect.Image.PullPolicy == "" { - cr.Spec.VLSelect.Image.PullPolicy = corev1.PullIfNotPresent - } - - if cr.Spec.VLSelect.UseDefaultResources == nil { - cr.Spec.VLSelect.UseDefaultResources = &c.VLCluster.UseDefaultResources - } - cr.Spec.VLSelect.Resources = Resources(cr.Spec.VLSelect.Resources, - config.Resource(c.VLCluster.Select.Resource), - *cr.Spec.VLSelect.UseDefaultResources, - ) + cv := config.ApplicationDefaults(c.VLCluster.Select) + addDefaultsToCommonParams(&cr.Spec.VLSelect.CommonAppsParams, &cp, &cv) } if cr.Spec.RequestsLoadBalancer.Enabled { - if cr.Spec.RequestsLoadBalancer.Spec.UseStrictSecurity == nil { - cr.Spec.RequestsLoadBalancer.Spec.UseStrictSecurity = &useStrictSecurity - } - if cr.Spec.RequestsLoadBalancer.Spec.DisableSelfServiceScrape == nil { - cr.Spec.RequestsLoadBalancer.Spec.DisableSelfServiceScrape = &c.DisableSelfServiceScrapeCreation - } - cr.Spec.RequestsLoadBalancer.Spec.ImagePullSecrets = append(cr.Spec.RequestsLoadBalancer.Spec.ImagePullSecrets, cr.Spec.ImagePullSecrets...) - - cv := config.ApplicationDefaults(c.VMAuth) - addDefaultsToCommonParams(&cr.Spec.RequestsLoadBalancer.Spec.CommonDefaultableParams, cr.Spec.License, &cv) - spec := &cr.Spec.RequestsLoadBalancer.Spec - if spec.EmbeddedProbes == nil { - spec.EmbeddedProbes = &vmv1beta1.EmbeddedProbes{} - } - if spec.StartupProbe == nil { - spec.StartupProbe = &corev1.Probe{ - ProbeHandler: corev1.ProbeHandler{ - HTTPGet: &corev1.HTTPGetAction{}, - }, - } - } - if spec.AdditionalServiceSpec != nil && !spec.AdditionalServiceSpec.UseAsDefault { - spec.AdditionalServiceSpec.UseAsDefault = true - } + addRequestsLoadBalancerDefaults(&cr.Spec.RequestsLoadBalancer, &cp) } } diff --git a/internal/controller/operator/factory/build/defaults_test.go b/internal/controller/operator/factory/build/defaults_test.go index a92afb0e1..95b0e6c3b 100644 --- a/internal/controller/operator/factory/build/defaults_test.go +++ b/internal/controller/operator/factory/build/defaults_test.go @@ -11,50 +11,192 @@ import ( ) func TestAddEnterpriseTagToAppCommonDefaults(t *testing.T) { - f := func(specVersion, defaultVersion string, hasLicense bool, wantVersion string) { + type opts struct { + specVersion, defaultVersion, wantVersion string + cp *commonParams + } + f := func(o opts) { t.Helper() - cdp := &vmv1beta1.CommonDefaultableParams{ + cdp := &vmv1beta1.CommonAppsParams{ Image: vmv1beta1.Image{ - Tag: specVersion, + Tag: o.specVersion, }, } appDefaults := &config.ApplicationDefaults{ - Version: defaultVersion, - } - var license *vmv1beta1.License - if hasLicense { - license = &vmv1beta1.License{ - Key: ptr.To("license-key-value"), - } + Version: o.defaultVersion, } - addDefaultsToCommonParams(cdp, license, appDefaults) - assert.Equal(t, wantVersion, cdp.Image.Tag, "unexpected spec version") + addDefaultsToCommonParams(cdp, o.cp, appDefaults) + assert.Equal(t, o.wantVersion, cdp.Image.Tag, "unexpected spec version") } // preserve spec version - f("v1.120.0", "", false, "v1.120.0") - f("v1.120.0", "v1.119.0", false, "v1.120.0") - f("v1.120.0", "v1.119.0", true, "v1.120.0") - f("v1.120.0", "", true, "v1.120.0") - f("v1.120.0-enterprise", "", true, "v1.120.0-enterprise") - f("v1.120.0-enterprise-cluster", "", true, "v1.120.0-enterprise-cluster") + f(opts{ + specVersion: "v1.120.0", + wantVersion: "v1.120.0", + }) + f(opts{ + specVersion: "v1.120.0", + defaultVersion: "v1.119.0", + wantVersion: "v1.120.0", + }) + f(opts{ + specVersion: "v1.120.0", + defaultVersion: "v1.119.0", + cp: &commonParams{ + license: &vmv1beta1.License{ + Key: ptr.To("license-key-value"), + }, + }, + wantVersion: "v1.120.0", + }) + f(opts{ + specVersion: "v1.120.0", + cp: &commonParams{ + license: &vmv1beta1.License{ + Key: ptr.To("license-key-value"), + }, + }, + wantVersion: "v1.120.0", + }) + f(opts{ + specVersion: "v1.120.0-enterprise", + cp: &commonParams{ + license: &vmv1beta1.License{ + Key: ptr.To("license-key-value"), + }, + }, + wantVersion: "v1.120.0-enterprise", + }) + f(opts{ + specVersion: "v1.120.0-enterprise-cluster", + cp: &commonParams{ + license: &vmv1beta1.License{ + Key: ptr.To("license-key-value"), + }, + }, + wantVersion: "v1.120.0-enterprise-cluster", + }) // change default value - f("", "v1.120.0", true, "v1.120.0-enterprise") - f("", "v1.120.0-cluster", true, "v1.120.0-enterprise-cluster") + f(opts{ + defaultVersion: "v1.120.0", + cp: &commonParams{ + license: &vmv1beta1.License{ + Key: ptr.To("license-key-value"), + }, + }, + wantVersion: "v1.120.0-enterprise", + }) + f(opts{ + defaultVersion: "v1.120.0-cluster", + cp: &commonParams{ + license: &vmv1beta1.License{ + Key: ptr.To("license-key-value"), + }, + }, + wantVersion: "v1.120.0-enterprise-cluster", + }) // preserve enterprise version - f("", "v1.120.0-enterprise-cluster", true, "v1.120.0-enterprise-cluster") - f("", "v1.120.0-enterprise", true, "v1.120.0-enterprise") + f(opts{ + defaultVersion: "v1.120.0-enterprise-cluster", + cp: &commonParams{ + license: &vmv1beta1.License{ + Key: ptr.To("license-key-value"), + }, + }, + wantVersion: "v1.120.0-enterprise-cluster", + }) + f(opts{ + defaultVersion: "v1.120.0-enterprise", + cp: &commonParams{ + license: &vmv1beta1.License{ + Key: ptr.To("license-key-value"), + }, + }, + wantVersion: "v1.120.0-enterprise", + }) // preserve unsupported tag - f("", "v1", true, "v1") - f("", "1.120.0", true, "1.120.0") - f("", "v1.120.0-rc1", true, "v1.120.0-rc1") - f("", "v1.120.0-enterprise-rc1", true, "v1.120.0-enterprise-rc1") - f("", "v1.120.0-enterprise-cluster-rc1", true, "v1.120.0-enterprise-cluster-rc1") - f("", "v1.120.0@sha256xxx", true, "v1.120.0@sha256xxx") - f("", "v1.120.0-enterprise@sha256xxx", true, "v1.120.0-enterprise@sha256xxx") - f("", "v1.120.0-cluster@sha256xxx", true, "v1.120.0-cluster@sha256xxx") - f("", "v1.120.0-enterprise-cluster@sha256xxx", true, "v1.120.0-enterprise-cluster@sha256xxx") + f(opts{ + defaultVersion: "v1", + cp: &commonParams{ + license: &vmv1beta1.License{ + Key: ptr.To("license-key-value"), + }, + }, + wantVersion: "v1", + }) + f(opts{ + defaultVersion: "1.120.0", + cp: &commonParams{ + license: &vmv1beta1.License{ + Key: ptr.To("license-key-value"), + }, + }, + wantVersion: "1.120.0", + }) + f(opts{ + defaultVersion: "v1.120.0-rc1", + cp: &commonParams{ + license: &vmv1beta1.License{ + Key: ptr.To("license-key-value"), + }, + }, + wantVersion: "v1.120.0-rc1", + }) + f(opts{ + defaultVersion: "v1.120.0-enterprise-rc1", + cp: &commonParams{ + license: &vmv1beta1.License{ + Key: ptr.To("license-key-value"), + }, + }, + wantVersion: "v1.120.0-enterprise-rc1", + }) + f(opts{ + defaultVersion: "v1.120.0-enterprise-cluster-rc1", + cp: &commonParams{ + license: &vmv1beta1.License{ + Key: ptr.To("license-key-value"), + }, + }, + wantVersion: "v1.120.0-enterprise-cluster-rc1", + }) + f(opts{ + defaultVersion: "v1.120.0@sha256xxx", + cp: &commonParams{ + license: &vmv1beta1.License{ + Key: ptr.To("license-key-value"), + }, + }, + wantVersion: "v1.120.0@sha256xxx", + }) + f(opts{ + defaultVersion: "v1.120.0-enterprise@sha256xxx", + cp: &commonParams{ + license: &vmv1beta1.License{ + Key: ptr.To("license-key-value"), + }, + }, + wantVersion: "v1.120.0-enterprise@sha256xxx", + }) + f(opts{ + defaultVersion: "v1.120.0-cluster@sha256xxx", + cp: &commonParams{ + license: &vmv1beta1.License{ + Key: ptr.To("license-key-value"), + }, + }, + wantVersion: "v1.120.0-cluster@sha256xxx", + }) + f(opts{ + defaultVersion: "v1.120.0-enterprise-cluster@sha256xxx", + cp: &commonParams{ + license: &vmv1beta1.License{ + Key: ptr.To("license-key-value"), + }, + }, + wantVersion: "v1.120.0-enterprise-cluster@sha256xxx", + }) } diff --git a/internal/controller/operator/factory/build/deployment.go b/internal/controller/operator/factory/build/deployment.go index 215e10fd1..5b38cc2ad 100644 --- a/internal/controller/operator/factory/build/deployment.go +++ b/internal/controller/operator/factory/build/deployment.go @@ -8,7 +8,7 @@ import ( ) // DeploymentAddCommonParams adds common params for all deployments -func DeploymentAddCommonParams(dst *appsv1.Deployment, useStrictSecurity bool, params *vmv1beta1.CommonApplicationDeploymentParams) { +func DeploymentAddCommonParams(dst *appsv1.Deployment, params *vmv1beta1.CommonAppsParams) { dst.Spec.Template.Spec.Affinity = params.Affinity dst.Spec.Template.Spec.Tolerations = params.Tolerations dst.Spec.Template.Spec.SchedulerName = params.SchedulerName @@ -22,11 +22,10 @@ func DeploymentAddCommonParams(dst *appsv1.Deployment, useStrictSecurity bool, p dst.Spec.Template.Spec.DNSPolicy = params.DNSPolicy dst.Spec.Template.Spec.DNSConfig = params.DNSConfig dst.Spec.Template.Spec.NodeSelector = params.NodeSelector - dst.Spec.Template.Spec.SecurityContext = AddStrictSecuritySettingsToPod(params.SecurityContext, useStrictSecurity) + dst.Spec.Template.Spec.SecurityContext = addStrictSecuritySettingsToPod(params) dst.Spec.Template.Spec.TerminationGracePeriodSeconds = params.TerminationGracePeriodSeconds dst.Spec.Template.Spec.TopologySpreadConstraints = params.TopologySpreadConstraints dst.Spec.Template.Spec.ImagePullSecrets = params.ImagePullSecrets - dst.Spec.Template.Spec.TerminationGracePeriodSeconds = params.TerminationGracePeriodSeconds dst.Spec.Template.Spec.ReadinessGates = params.ReadinessGates dst.Spec.MinReadySeconds = params.MinReadySeconds dst.Spec.Replicas = params.ReplicaCount diff --git a/internal/controller/operator/factory/build/security.go b/internal/controller/operator/factory/build/security.go index 5ae76710c..f27f10bf4 100644 --- a/internal/controller/operator/factory/build/security.go +++ b/internal/controller/operator/factory/build/security.go @@ -53,24 +53,24 @@ var ( ) // AddStrictSecuritySettingsToContainers conditionally adds Security settings to given containers -func AddStrictSecuritySettingsToContainers(p *vmv1beta1.SecurityContext, containers []corev1.Container, enableStrictSecurity bool) { - if !enableStrictSecurity && p == nil { +func AddStrictSecuritySettingsToContainers(containers []corev1.Container, params *vmv1beta1.CommonAppsParams) { + if !ptr.Deref(params.UseStrictSecurity, false) && (params == nil || params.SecurityContext == nil) { return } for idx := range containers { container := &containers[idx] - container.SecurityContext = containerSecurityContext(p, false) + container.SecurityContext = containerSecurityContext(params.SecurityContext, false) } } // AddStrictSecuritySettingsWithRootToContainers conditionally adds Security settings to given containers -func AddStrictSecuritySettingsWithRootToContainers(p *vmv1beta1.SecurityContext, containers []corev1.Container, enableStrictSecurity bool) { - if !enableStrictSecurity && p == nil { +func AddStrictSecuritySettingsWithRootToContainers(containers []corev1.Container, params *vmv1beta1.CommonAppsParams) { + if !ptr.Deref(params.UseStrictSecurity, false) && (params == nil || params.SecurityContext == nil) { return } for idx := range containers { container := &containers[idx] - container.SecurityContext = containerSecurityContext(p, true) + container.SecurityContext = containerSecurityContext(params.SecurityContext, true) } } @@ -97,12 +97,12 @@ func containerSecurityContext(p *vmv1beta1.SecurityContext, requireRoot bool) *c return &sc } -// AddStrictSecuritySettingsToPod conditionally creates security context for pod or returns predefined one -func AddStrictSecuritySettingsToPod(p *vmv1beta1.SecurityContext, enableStrictSecurity bool) *corev1.PodSecurityContext { - if p != nil { - return p.PodSecurityContext +// addStrictSecuritySettingsToPod conditionally creates security context for pod or returns predefined one +func addStrictSecuritySettingsToPod(params *vmv1beta1.CommonAppsParams) *corev1.PodSecurityContext { + if params != nil && params.SecurityContext != nil { + return params.SecurityContext.PodSecurityContext } - if !enableStrictSecurity { + if !ptr.Deref(params.UseStrictSecurity, false) { return nil } securityContext := getDefaultPodSecurityContext(false) @@ -113,12 +113,12 @@ func AddStrictSecuritySettingsToPod(p *vmv1beta1.SecurityContext, enableStrictSe return securityContext } -// AddStrictSecuritySettingsWithRootToPod conditionally creates security context for pod or returns predefined one -func AddStrictSecuritySettingsWithRootToPod(p *vmv1beta1.SecurityContext, enableStrictSecurity bool) *corev1.PodSecurityContext { - if p != nil { - return p.PodSecurityContext +// addStrictSecuritySettingsWithRootToPod conditionally creates security context for pod or returns predefined one +func addStrictSecuritySettingsWithRootToPod(params *vmv1beta1.CommonAppsParams) *corev1.PodSecurityContext { + if params != nil && params.SecurityContext != nil { + return params.SecurityContext.PodSecurityContext } - if !enableStrictSecurity { + if !ptr.Deref(params.UseStrictSecurity, false) { return nil } securityContext := getDefaultPodSecurityContext(true) diff --git a/internal/controller/operator/factory/build/security_test.go b/internal/controller/operator/factory/build/security_test.go index a24a5bfd2..c48d4f49e 100644 --- a/internal/controller/operator/factory/build/security_test.go +++ b/internal/controller/operator/factory/build/security_test.go @@ -14,10 +14,9 @@ import ( func TestAddStrictSecuritySettingsToPod(t *testing.T) { type opts struct { - psp *vmv1beta1.SecurityContext - enableStrictSecurity bool - expected *corev1.PodSecurityContext - kubeletVersion version.Info + params *vmv1beta1.CommonAppsParams + expected *corev1.PodSecurityContext + kubeletVersion version.Info } f := func(o opts) { @@ -28,13 +27,15 @@ func TestAddStrictSecuritySettingsToPod(t *testing.T) { restoreVersion := version.Info{Major: "0", Minor: "0"} assert.NoError(t, k8stools.SetKubernetesVersionWithDefaults(&restoreVersion, 0, 0)) }() - res := AddStrictSecuritySettingsToPod(o.psp, o.enableStrictSecurity) + res := addStrictSecuritySettingsToPod(o.params) assert.Equal(t, res, o.expected) } // enforce strict security f(opts{ - enableStrictSecurity: true, + params: &vmv1beta1.CommonAppsParams{ + UseStrictSecurity: ptr.To(true), + }, expected: &corev1.PodSecurityContext{ RunAsNonRoot: ptr.To(true), RunAsUser: ptr.To(int64(65534)), @@ -50,20 +51,24 @@ func TestAddStrictSecuritySettingsToPod(t *testing.T) { // disable enableStrictSecurity f(opts{ - enableStrictSecurity: false, - expected: nil, - kubeletVersion: version.Info{Major: "1", Minor: "27"}, + params: &vmv1beta1.CommonAppsParams{ + UseStrictSecurity: ptr.To(false), + }, + expected: nil, + kubeletVersion: version.Info{Major: "1", Minor: "27"}, }) // use custom security f(opts{ - psp: &vmv1beta1.SecurityContext{ - PodSecurityContext: &corev1.PodSecurityContext{ - RunAsNonRoot: ptr.To(false), + params: &vmv1beta1.CommonAppsParams{ + UseStrictSecurity: ptr.To(true), + SecurityContext: &vmv1beta1.SecurityContext{ + PodSecurityContext: &corev1.PodSecurityContext{ + RunAsNonRoot: ptr.To(false), + }, + ContainerSecurityContext: nil, }, - ContainerSecurityContext: nil, }, - enableStrictSecurity: true, expected: &corev1.PodSecurityContext{ RunAsNonRoot: ptr.To(false), }, @@ -73,21 +78,22 @@ func TestAddStrictSecuritySettingsToPod(t *testing.T) { func TestAddStrictSecuritySettingsToContainers(t *testing.T) { type opts struct { - sc *vmv1beta1.SecurityContext - containers []corev1.Container - useStrictSecurity bool - expected []corev1.Container + params *vmv1beta1.CommonAppsParams + containers []corev1.Container + expected []corev1.Container } f := func(o opts) { t.Helper() - AddStrictSecuritySettingsToContainers(o.sc, o.containers, o.useStrictSecurity) + AddStrictSecuritySettingsToContainers(o.containers, o.params) assert.Equal(t, o.expected, o.containers) } // default security f(opts{ - useStrictSecurity: true, + params: &vmv1beta1.CommonAppsParams{ + UseStrictSecurity: ptr.To(true), + }, containers: []corev1.Container{ { Name: "c1", @@ -110,14 +116,16 @@ func TestAddStrictSecuritySettingsToContainers(t *testing.T) { // add from spec f(opts{ - useStrictSecurity: true, - sc: &vmv1beta1.SecurityContext{ - PodSecurityContext: &corev1.PodSecurityContext{ - RunAsUser: ptr.To[int64](1), - RunAsNonRoot: ptr.To(false), - }, - ContainerSecurityContext: &vmv1beta1.ContainerSecurityContext{ - Privileged: ptr.To(true), + params: &vmv1beta1.CommonAppsParams{ + UseStrictSecurity: ptr.To(true), + SecurityContext: &vmv1beta1.SecurityContext{ + PodSecurityContext: &corev1.PodSecurityContext{ + RunAsUser: ptr.To[int64](1), + RunAsNonRoot: ptr.To(false), + }, + ContainerSecurityContext: &vmv1beta1.ContainerSecurityContext{ + Privileged: ptr.To(true), + }, }, }, containers: []corev1.Container{ @@ -150,7 +158,9 @@ func TestAddStrictSecuritySettingsToContainers(t *testing.T) { // replace defined context f(opts{ - useStrictSecurity: true, + params: &vmv1beta1.CommonAppsParams{ + UseStrictSecurity: ptr.To(true), + }, containers: []corev1.Container{ { Name: "c1", @@ -181,7 +191,9 @@ func TestAddStrictSecuritySettingsToContainers(t *testing.T) { // replace partial security context f(opts{ - useStrictSecurity: true, + params: &vmv1beta1.CommonAppsParams{ + UseStrictSecurity: ptr.To(true), + }, containers: []corev1.Container{ { Name: "c1", @@ -207,10 +219,12 @@ func TestAddStrictSecuritySettingsToContainers(t *testing.T) { // replace security context if external defined f(opts{ - useStrictSecurity: true, - sc: &vmv1beta1.SecurityContext{ - PodSecurityContext: &corev1.PodSecurityContext{ - RunAsUser: ptr.To[int64](1000), + params: &vmv1beta1.CommonAppsParams{ + UseStrictSecurity: ptr.To(true), + SecurityContext: &vmv1beta1.SecurityContext{ + PodSecurityContext: &corev1.PodSecurityContext{ + RunAsUser: ptr.To[int64](1000), + }, }, }, containers: []corev1.Container{ @@ -242,7 +256,9 @@ func TestAddStrictSecuritySettingsToContainers(t *testing.T) { // insecure mode f(opts{ - useStrictSecurity: false, + params: &vmv1beta1.CommonAppsParams{ + UseStrictSecurity: ptr.To(false), + }, containers: []corev1.Container{ { Name: "c1", @@ -263,10 +279,12 @@ func TestAddStrictSecuritySettingsToContainers(t *testing.T) { // add external if useStrict is false f(opts{ - useStrictSecurity: false, - sc: &vmv1beta1.SecurityContext{ - PodSecurityContext: &corev1.PodSecurityContext{ - RunAsUser: ptr.To[int64](1000), + params: &vmv1beta1.CommonAppsParams{ + UseStrictSecurity: ptr.To(false), + SecurityContext: &vmv1beta1.SecurityContext{ + PodSecurityContext: &corev1.PodSecurityContext{ + RunAsUser: ptr.To[int64](1000), + }, }, }, containers: []corev1.Container{ @@ -295,10 +313,12 @@ func TestAddStrictSecuritySettingsToContainers(t *testing.T) { // replace with external if useStrict is false f(opts{ - useStrictSecurity: false, - sc: &vmv1beta1.SecurityContext{ - PodSecurityContext: &corev1.PodSecurityContext{ - RunAsUser: ptr.To[int64](1000), + params: &vmv1beta1.CommonAppsParams{ + UseStrictSecurity: ptr.To(false), + SecurityContext: &vmv1beta1.SecurityContext{ + PodSecurityContext: &corev1.PodSecurityContext{ + RunAsUser: ptr.To[int64](1000), + }, }, }, containers: []corev1.Container{ diff --git a/internal/controller/operator/factory/build/service_account.go b/internal/controller/operator/factory/build/service_account.go index a3275c3c3..d1b3631dc 100644 --- a/internal/controller/operator/factory/build/service_account.go +++ b/internal/controller/operator/factory/build/service_account.go @@ -51,7 +51,7 @@ func AddServiceAccountTokenVolumeMount(dst *corev1.Container, automount bool) { } // AddServiceAccountTokenVolume conditionally adds volume "kube-api-access" with ServiceAccountToken projection -func AddServiceAccountTokenVolume(dst []corev1.Volume, params *vmv1beta1.CommonApplicationDeploymentParams) []corev1.Volume { +func AddServiceAccountTokenVolume(dst []corev1.Volume, params *vmv1beta1.CommonAppsParams) []corev1.Volume { if !params.DisableAutomountServiceAccountToken { return dst } diff --git a/internal/controller/operator/factory/build/statefulset.go b/internal/controller/operator/factory/build/statefulset.go index 172fa55f7..a7b19574e 100644 --- a/internal/controller/operator/factory/build/statefulset.go +++ b/internal/controller/operator/factory/build/statefulset.go @@ -8,7 +8,7 @@ import ( ) // StatefulSetAddCommonParams adds common params to given statefulset -func StatefulSetAddCommonParams(dst *appsv1.StatefulSet, useStrictSecurity bool, params *vmv1beta1.CommonApplicationDeploymentParams) { +func StatefulSetAddCommonParams(dst *appsv1.StatefulSet, params *vmv1beta1.CommonAppsParams) { dst.Spec.Template.Spec.Affinity = params.Affinity dst.Spec.Template.Spec.Tolerations = params.Tolerations dst.Spec.Template.Spec.SchedulerName = params.SchedulerName @@ -22,11 +22,10 @@ func StatefulSetAddCommonParams(dst *appsv1.StatefulSet, useStrictSecurity bool, dst.Spec.Template.Spec.DNSPolicy = params.DNSPolicy dst.Spec.Template.Spec.DNSConfig = params.DNSConfig dst.Spec.Template.Spec.NodeSelector = params.NodeSelector - dst.Spec.Template.Spec.SecurityContext = AddStrictSecuritySettingsToPod(params.SecurityContext, useStrictSecurity) + dst.Spec.Template.Spec.SecurityContext = addStrictSecuritySettingsToPod(params) dst.Spec.Template.Spec.TerminationGracePeriodSeconds = params.TerminationGracePeriodSeconds dst.Spec.Template.Spec.TopologySpreadConstraints = params.TopologySpreadConstraints dst.Spec.Template.Spec.ImagePullSecrets = params.ImagePullSecrets - dst.Spec.Template.Spec.TerminationGracePeriodSeconds = params.TerminationGracePeriodSeconds dst.Spec.Template.Spec.ReadinessGates = params.ReadinessGates dst.Spec.MinReadySeconds = params.MinReadySeconds dst.Spec.Replicas = params.ReplicaCount diff --git a/internal/controller/operator/factory/reconcile/service.go b/internal/controller/operator/factory/reconcile/service.go index 220904a09..0f5c766e4 100644 --- a/internal/controller/operator/factory/reconcile/service.go +++ b/internal/controller/operator/factory/reconcile/service.go @@ -114,7 +114,11 @@ func reconcileService(ctx context.Context, rclient client.Client, newObj, prevOb } } + // Normalize both old and new objects with scheme defaults + // this ensures fields set on admission (e.g. sessionAffinity, + // internalTrafficPolicy) are defaulted and no extra Update is triggered rclient.Scheme().Default(newObj) + rclient.Scheme().Default(&existingObj) metaChanged, err := mergeMeta(&existingObj, newObj, prevMeta, owner, removeFinalizer) if err != nil { return err diff --git a/internal/controller/operator/factory/reconcile/vmagent_test.go b/internal/controller/operator/factory/reconcile/vmagent_test.go index 97bab2aed..f9d003867 100644 --- a/internal/controller/operator/factory/reconcile/vmagent_test.go +++ b/internal/controller/operator/factory/reconcile/vmagent_test.go @@ -30,7 +30,7 @@ func TestVMAgentReconcile(t *testing.T) { Namespace: "default", }, Spec: vmv1beta1.VMAgentSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), }, }, diff --git a/internal/controller/operator/factory/reconcile/vmauth_test.go b/internal/controller/operator/factory/reconcile/vmauth_test.go index e7c8f76d7..17358abb6 100644 --- a/internal/controller/operator/factory/reconcile/vmauth_test.go +++ b/internal/controller/operator/factory/reconcile/vmauth_test.go @@ -28,7 +28,7 @@ func TestVMAuthReconcile(t *testing.T) { Namespace: "default", }, Spec: vmv1beta1.VMAuthSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), }, }, diff --git a/internal/controller/operator/factory/vlagent/vlagent.go b/internal/controller/operator/factory/vlagent/vlagent.go index a02aaad1c..6654ef576 100644 --- a/internal/controller/operator/factory/vlagent/vlagent.go +++ b/internal/controller/operator/factory/vlagent/vlagent.go @@ -179,8 +179,6 @@ func newK8sApp(cr *vmv1.VLAgent) (client.Object, error) { if err != nil { return nil, err } - useStrictSecurity := ptr.Deref(cr.Spec.UseStrictSecurity, false) - if cr.Spec.K8sCollector.Enabled { dsSpec := &appsv1.DaemonSet{ ObjectMeta: metav1.ObjectMeta{ @@ -204,8 +202,8 @@ func newK8sApp(cr *vmv1.VLAgent) (client.Object, error) { }, }, } - build.DaemonSetAddCommonParams(dsSpec, useStrictSecurity, &cr.Spec.CommonApplicationDeploymentParams) - dsSpec.Spec.Template.Spec.Volumes = build.AddServiceAccountTokenVolume(dsSpec.Spec.Template.Spec.Volumes, &cr.Spec.CommonApplicationDeploymentParams) + build.DaemonSetAddCommonParams(dsSpec, &cr.Spec.CommonAppsParams) + dsSpec.Spec.Template.Spec.Volumes = build.AddServiceAccountTokenVolume(dsSpec.Spec.Template.Spec.Volumes, &cr.Spec.CommonAppsParams) return dsSpec, nil } stsSpec := &appsv1.StatefulSet{ @@ -238,7 +236,7 @@ func newK8sApp(cr *vmv1.VLAgent) (client.Object, error) { if cr.Spec.PersistentVolumeClaimRetentionPolicy != nil { stsSpec.Spec.PersistentVolumeClaimRetentionPolicy = cr.Spec.PersistentVolumeClaimRetentionPolicy } - build.StatefulSetAddCommonParams(stsSpec, useStrictSecurity, &cr.Spec.CommonApplicationDeploymentParams) + build.StatefulSetAddCommonParams(stsSpec, &cr.Spec.CommonAppsParams) if cr.Spec.TmpDataPath == nil { cr.Spec.Storage.IntoSTSVolume(tmpDataVolumeName, &stsSpec.Spec) @@ -441,9 +439,7 @@ func newPodSpec(cr *vmv1.VLAgent) (*corev1.PodSpec, error) { TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, } - useStrictSecurity := ptr.Deref(cr.Spec.UseStrictSecurity, false) - - vlagentContainer = build.Probe(vlagentContainer, cr) + build.Probe(&vlagentContainer, cr, &cr.Spec.CommonAppsParams) var operatorContainers []corev1.Container var ic []corev1.Container var err error @@ -454,9 +450,9 @@ func newPodSpec(cr *vmv1.VLAgent) (*corev1.PodSpec, error) { operatorContainers = append(operatorContainers, vlagentContainer) if cr.Spec.K8sCollector.Enabled { - build.AddStrictSecuritySettingsWithRootToContainers(cr.Spec.SecurityContext, operatorContainers, useStrictSecurity) + build.AddStrictSecuritySettingsWithRootToContainers(operatorContainers, &cr.Spec.CommonAppsParams) } else { - build.AddStrictSecuritySettingsToContainers(cr.Spec.SecurityContext, operatorContainers, useStrictSecurity) + build.AddStrictSecuritySettingsToContainers(operatorContainers, &cr.Spec.CommonAppsParams) } containers, err := k8stools.MergePatchContainers(operatorContainers, cr.Spec.Containers) diff --git a/internal/controller/operator/factory/vlagent/vlagent_test.go b/internal/controller/operator/factory/vlagent/vlagent_test.go index c8856f2c2..2bacd3917 100644 --- a/internal/controller/operator/factory/vlagent/vlagent_test.go +++ b/internal/controller/operator/factory/vlagent/vlagent_test.go @@ -52,10 +52,9 @@ func TestCreateOrUpdate(t *testing.T) { RemoteWrite: []vmv1.VLAgentRemoteWriteSpec{ {URL: "http://remote-write"}, }, - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(0)), }, - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{}, Storage: &vmv1beta1.StorageSpec{ VolumeClaimTemplate: vmv1beta1.EmbeddedPersistentVolumeClaim{ Spec: corev1.PersistentVolumeClaimSpec{ @@ -111,7 +110,7 @@ func TestCreateOrUpdate(t *testing.T) { Namespace: "default", }, Spec: vmv1.VLAgentSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(0)), }, RemoteWrite: []vmv1.VLAgentRemoteWriteSpec{ @@ -191,7 +190,7 @@ func TestCreateOrUpdate(t *testing.T) { RemoteWrite: []vmv1.VLAgentRemoteWriteSpec{ {URL: "http://remote-write"}, }, - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), }, Storage: &vmv1beta1.StorageSpec{ @@ -249,7 +248,7 @@ func TestCreateOrUpdate(t *testing.T) { Namespace: "default", }, Spec: vmv1.VLAgentSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(0)), }, RemoteWrite: []vmv1.VLAgentRemoteWriteSpec{ @@ -752,7 +751,7 @@ func TestMakeSpecForAgentOk(t *testing.T) { f(&vmv1.VLAgent{ ObjectMeta: metav1.ObjectMeta{Name: "agent", Namespace: "default"}, Spec: vmv1.VLAgentSpec{ - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ Image: vmv1beta1.Image{ Repository: "vm-repo", Tag: "v1.97.1", @@ -826,7 +825,7 @@ serviceaccountname: vlagent-agent f(&vmv1.VLAgent{ ObjectMeta: metav1.ObjectMeta{Name: "agent", Namespace: "default"}, Spec: vmv1.VLAgentSpec{ - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ Image: vmv1beta1.Image{ Tag: "v1.97.1", }, @@ -879,7 +878,7 @@ serviceaccountname: vlagent-agent f(&vmv1.VLAgent{ ObjectMeta: metav1.ObjectMeta{Name: "agent", Namespace: "default"}, Spec: vmv1.VLAgentSpec{ - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ Image: vmv1beta1.Image{ Tag: "v1.97.1", }, @@ -948,9 +947,9 @@ serviceaccountname: vlagent-agent f(&vmv1.VLAgent{ ObjectMeta: metav1.ObjectMeta{Name: "agent", Namespace: "default"}, Spec: vmv1.VLAgentSpec{ - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ Image: vmv1beta1.Image{ - Tag: "v1.47.0", + Tag: "v1.48.0", }, UseDefaultResources: ptr.To(false), Port: "9425", @@ -976,7 +975,7 @@ serviceaccountname: vlagent-agent }, []runtime.Object{}, ` containers: - name: vlagent - image: victoriametrics/vlagent:v1.47.0 + image: victoriametrics/vlagent:v1.48.0 args: - -httpListenAddr=:9425 - -kubernetesCollector @@ -1043,7 +1042,7 @@ volumes: f(&vmv1.VLAgent{ ObjectMeta: metav1.ObjectMeta{Name: "agent", Namespace: "default"}, Spec: vmv1.VLAgentSpec{ - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ Image: vmv1beta1.Image{ Tag: "v1.97.1", }, @@ -1114,14 +1113,12 @@ serviceaccountname: vlagent-agent f(&vmv1.VLAgent{ ObjectMeta: metav1.ObjectMeta{Name: "agent", Namespace: "default"}, Spec: vmv1.VLAgentSpec{ - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ Image: vmv1beta1.Image{ Tag: "v0.0.1", }, UseDefaultResources: ptr.To(false), Port: "9425", - }, - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ ExtraArgs: map[string]string{ "remoteWrite.maxDiskUsagePerURL": "35GiB", }, @@ -1186,5 +1183,4 @@ containers: serviceaccountname: vlagent-agent `) - } diff --git a/internal/controller/operator/factory/vlcluster/vlcluster_test.go b/internal/controller/operator/factory/vlcluster/vlcluster_test.go index 0dffe3c1e..0bb916bf9 100644 --- a/internal/controller/operator/factory/vlcluster/vlcluster_test.go +++ b/internal/controller/operator/factory/vlcluster/vlcluster_test.go @@ -72,17 +72,17 @@ func TestCreateOrUpdate(t *testing.T) { }, Spec: vmv1.VLClusterSpec{ VLInsert: &vmv1.VLInsert{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(2)), }, }, VLStorage: &vmv1.VLStorage{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(2)), }, }, VLSelect: &vmv1.VLSelect{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(2)), }, }, @@ -135,7 +135,7 @@ func TestCreateOrUpdate(t *testing.T) { RetentionPeriod: "1w", RetentionMaxDiskSpaceUsageBytes: "5GB", FutureRetention: "2d", - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), }, }, @@ -165,13 +165,13 @@ func TestCreateOrUpdate(t *testing.T) { Addr: "localhost:10101", }, }, - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), }, }, VLStorage: &vmv1.VLStorage{ RetentionPeriod: "1w", - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), }, }, @@ -205,13 +205,13 @@ func TestCreateOrUpdate(t *testing.T) { Addr: "localhost:10101", }, }, - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), }, }, VLStorage: &vmv1.VLStorage{ RetentionPeriod: "1w", - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), }, HPA: &vmv1beta1.EmbeddedHPA{ @@ -233,7 +233,7 @@ func TestCreateOrUpdate(t *testing.T) { ObjectMeta: metav1.ObjectMeta{Name: "test", Namespace: "default"}, Spec: vmv1.VLClusterSpec{ VLInsert: &vmv1.VLInsert{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(0)), }, VPA: &vmv1beta1.EmbeddedVPA{ @@ -290,7 +290,7 @@ func TestCreateOrUpdate(t *testing.T) { ObjectMeta: metav1.ObjectMeta{Name: "test", Namespace: "default"}, Spec: vmv1.VLClusterSpec{ VLSelect: &vmv1.VLSelect{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(0)), }, VPA: &vmv1beta1.EmbeddedVPA{ @@ -358,7 +358,7 @@ func TestCreateOrUpdate(t *testing.T) { ObjectMeta: metav1.ObjectMeta{Name: "test", Namespace: "default"}, Spec: vmv1.VLClusterSpec{ VLStorage: &vmv1.VLStorage{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(0)), }, VPA: &vmv1beta1.EmbeddedVPA{ @@ -439,7 +439,7 @@ func TestCreateOrUpdate(t *testing.T) { ObjectMeta: metav1.ObjectMeta{Name: "test", Namespace: "default"}, Spec: vmv1.VLClusterSpec{ VLInsert: &vmv1.VLInsert{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(0)), }, VPA: &vmv1beta1.EmbeddedVPA{ @@ -532,7 +532,7 @@ func TestCreateOrUpdate(t *testing.T) { }, Spec: vmv1.VLClusterSpec{ VLInsert: &vmv1.VLInsert{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(0)), }, }, diff --git a/internal/controller/operator/factory/vlcluster/vlinsert.go b/internal/controller/operator/factory/vlcluster/vlinsert.go index b748bd2dc..06931cfbb 100644 --- a/internal/controller/operator/factory/vlcluster/vlinsert.go +++ b/internal/controller/operator/factory/vlcluster/vlinsert.go @@ -111,7 +111,7 @@ func buildVLInsertDeployment(cr *vmv1.VLCluster) (*appsv1.Deployment, error) { Template: *podSpec, }, } - build.DeploymentAddCommonParams(stsSpec, ptr.Deref(cr.Spec.VLInsert.UseStrictSecurity, false), &cr.Spec.VLInsert.CommonApplicationDeploymentParams) + build.DeploymentAddCommonParams(stsSpec, &cr.Spec.VLInsert.CommonAppsParams) return stsSpec, nil } @@ -222,10 +222,10 @@ func buildVLInsertPodSpec(cr *vmv1.VLCluster) (*corev1.PodTemplateSpec, error) { TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, } - insertContainers = build.Probe(insertContainers, cr.Spec.VLInsert) + build.Probe(&insertContainers, cr.Spec.VLInsert, &cr.Spec.VLInsert.CommonAppsParams) operatorContainers := []corev1.Container{insertContainers} - build.AddStrictSecuritySettingsToContainers(cr.Spec.VLInsert.SecurityContext, operatorContainers, ptr.Deref(cr.Spec.VLInsert.UseStrictSecurity, false)) + build.AddStrictSecuritySettingsToContainers(operatorContainers, &cr.Spec.VLInsert.CommonAppsParams) containers, err := k8stools.MergePatchContainers(operatorContainers, cr.Spec.VLInsert.Containers) if err != nil { return nil, err diff --git a/internal/controller/operator/factory/vlcluster/vlselect.go b/internal/controller/operator/factory/vlcluster/vlselect.go index c3d3bf840..77cdf4e26 100644 --- a/internal/controller/operator/factory/vlcluster/vlselect.go +++ b/internal/controller/operator/factory/vlcluster/vlselect.go @@ -213,7 +213,7 @@ func buildVLSelectDeployment(cr *vmv1.VLCluster) (*appsv1.Deployment, error) { Template: *podSpec, }, } - build.DeploymentAddCommonParams(depSpec, ptr.Deref(cr.Spec.VLSelect.UseStrictSecurity, false), &cr.Spec.VLSelect.CommonApplicationDeploymentParams) + build.DeploymentAddCommonParams(depSpec, &cr.Spec.VLSelect.CommonAppsParams) return depSpec, nil } @@ -323,10 +323,10 @@ func buildVLSelectPodSpec(cr *vmv1.VLCluster) (*corev1.PodTemplateSpec, error) { TerminationMessagePath: "/dev/termination-log", } - selectContainers = build.Probe(selectContainers, cr.Spec.VLSelect) + build.Probe(&selectContainers, cr.Spec.VLSelect, &cr.Spec.VLSelect.CommonAppsParams) operatorContainers := []corev1.Container{selectContainers} - build.AddStrictSecuritySettingsToContainers(cr.Spec.VLSelect.SecurityContext, operatorContainers, ptr.Deref(cr.Spec.VLSelect.UseStrictSecurity, false)) + build.AddStrictSecuritySettingsToContainers(operatorContainers, &cr.Spec.VLSelect.CommonAppsParams) containers, err := k8stools.MergePatchContainers(operatorContainers, cr.Spec.VLSelect.Containers) if err != nil { return nil, err diff --git a/internal/controller/operator/factory/vlcluster/vlstorage.go b/internal/controller/operator/factory/vlcluster/vlstorage.go index 644b130fb..48679d8ff 100644 --- a/internal/controller/operator/factory/vlcluster/vlstorage.go +++ b/internal/controller/operator/factory/vlcluster/vlstorage.go @@ -206,7 +206,7 @@ func buildVLStorageSTSSpec(cr *vmv1.VLCluster) (*appsv1.StatefulSet, error) { if cr.Spec.VLStorage.PersistentVolumeClaimRetentionPolicy != nil { stsSpec.Spec.PersistentVolumeClaimRetentionPolicy = cr.Spec.VLStorage.PersistentVolumeClaimRetentionPolicy } - build.StatefulSetAddCommonParams(stsSpec, ptr.Deref(cr.Spec.VLStorage.UseStrictSecurity, false), &cr.Spec.VLStorage.CommonApplicationDeploymentParams) + build.StatefulSetAddCommonParams(stsSpec, &cr.Spec.VLStorage.CommonAppsParams) storageSpec := cr.Spec.VLStorage.Storage storageSpec.IntoSTSVolume(cr.Spec.VLStorage.GetStorageVolumeName(), &stsSpec.Spec) stsSpec.Spec.VolumeClaimTemplates = append(stsSpec.Spec.VolumeClaimTemplates, cr.Spec.VLStorage.ClaimTemplates...) @@ -325,19 +325,18 @@ func buildVLStoragePodSpec(cr *vmv1.VLCluster) (*corev1.PodTemplateSpec, error) TerminationMessagePath: "/dev/termination-log", } - vmstorageContainer = build.Probe(vmstorageContainer, cr.Spec.VLStorage) + build.Probe(&vmstorageContainer, cr.Spec.VLStorage, &cr.Spec.VLStorage.CommonAppsParams) storageContainers := []corev1.Container{vmstorageContainer} var initContainers []corev1.Container - useStrictSecurity := ptr.Deref(cr.Spec.VLStorage.UseStrictSecurity, false) - build.AddStrictSecuritySettingsToContainers(cr.Spec.VLStorage.SecurityContext, initContainers, useStrictSecurity) + build.AddStrictSecuritySettingsToContainers(initContainers, &cr.Spec.VLStorage.CommonAppsParams) ic, err := k8stools.MergePatchContainers(initContainers, cr.Spec.VLStorage.InitContainers) if err != nil { return nil, fmt.Errorf("cannot patch storage init containers: %w", err) } - build.AddStrictSecuritySettingsToContainers(cr.Spec.VLStorage.SecurityContext, storageContainers, useStrictSecurity) + build.AddStrictSecuritySettingsToContainers(storageContainers, &cr.Spec.VLStorage.CommonAppsParams) containers, err := k8stools.MergePatchContainers(storageContainers, cr.Spec.VLStorage.Containers) if err != nil { return nil, fmt.Errorf("cannot patch storage containers: %w", err) diff --git a/internal/controller/operator/factory/vlcluster/vmauth_lb.go b/internal/controller/operator/factory/vlcluster/vmauth_lb.go index ee3833c46..f7fd19f71 100644 --- a/internal/controller/operator/factory/vlcluster/vmauth_lb.go +++ b/internal/controller/operator/factory/vlcluster/vmauth_lb.go @@ -180,13 +180,13 @@ func buildVMauthLBDeployment(cr *vmv1.VLCluster) (*appsv1.Deployment, error) { ImagePullPolicy: spec.Image.PullPolicy, VolumeMounts: vmMounts, } - vmauthLBCnt = build.Probe(vmauthLBCnt, &spec) + build.Probe(&vmauthLBCnt, &spec, &spec.CommonAppsParams) containers := []corev1.Container{ vmauthLBCnt, } var err error - build.AddStrictSecuritySettingsToContainers(spec.SecurityContext, containers, ptr.Deref(spec.UseStrictSecurity, false)) + build.AddStrictSecuritySettingsToContainers(containers, &spec.CommonAppsParams) containers, err = k8stools.MergePatchContainers(containers, spec.Containers) if err != nil { return nil, fmt.Errorf("cannot patch containers: %w", err) @@ -225,7 +225,7 @@ func buildVMauthLBDeployment(cr *vmv1.VLCluster) (*appsv1.Deployment, error) { }, }, } - build.DeploymentAddCommonParams(lbDep, ptr.Deref(cr.Spec.RequestsLoadBalancer.Spec.UseStrictSecurity, false), &spec.CommonApplicationDeploymentParams) + build.DeploymentAddCommonParams(lbDep, &spec.CommonAppsParams) return lbDep, nil diff --git a/internal/controller/operator/factory/vlsingle/vlsingle.go b/internal/controller/operator/factory/vlsingle/vlsingle.go index 90248fba4..89786210a 100644 --- a/internal/controller/operator/factory/vlsingle/vlsingle.go +++ b/internal/controller/operator/factory/vlsingle/vlsingle.go @@ -137,7 +137,7 @@ func newDeployment(r *vmv1.VLSingle) (*appsv1.Deployment, error) { Template: *podSpec, }, } - build.DeploymentAddCommonParams(depSpec, ptr.Deref(r.Spec.UseStrictSecurity, false), &r.Spec.CommonApplicationDeploymentParams) + build.DeploymentAddCommonParams(depSpec, &r.Spec.CommonAppsParams) return depSpec, nil } @@ -254,11 +254,11 @@ func makePodSpec(r *vmv1.VLSingle) (*corev1.PodTemplateSpec, error) { ImagePullPolicy: r.Spec.Image.PullPolicy, } - vlsingleContainer = build.Probe(vlsingleContainer, r) + build.Probe(&vlsingleContainer, r, &r.Spec.CommonAppsParams) operatorContainers := []corev1.Container{vlsingleContainer} - build.AddStrictSecuritySettingsToContainers(r.Spec.SecurityContext, operatorContainers, ptr.Deref(r.Spec.UseStrictSecurity, false)) + build.AddStrictSecuritySettingsToContainers(operatorContainers, &r.Spec.CommonAppsParams) containers, err := k8stools.MergePatchContainers(operatorContainers, r.Spec.Containers) if err != nil { diff --git a/internal/controller/operator/factory/vlsingle/vlsingle_test.go b/internal/controller/operator/factory/vlsingle/vlsingle_test.go index 57eace67b..fac9d558a 100644 --- a/internal/controller/operator/factory/vlsingle/vlsingle_test.go +++ b/internal/controller/operator/factory/vlsingle/vlsingle_test.go @@ -48,7 +48,7 @@ func TestCreateOrUpdateVLSingle(t *testing.T) { Namespace: "default", }, Spec: vmv1.VLSingleSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), }, }, @@ -82,11 +82,9 @@ func TestCreateOrUpdateVLSingle(t *testing.T) { }, Spec: vmv1.VLSingleSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), - }, - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ - Port: "8435", + Port: "8435", }, }, }, @@ -118,11 +116,9 @@ func TestCreateOrUpdateVLSingle(t *testing.T) { Namespace: "default", }, Spec: vmv1.VLSingleSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), - }, - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ - Port: "8435", + Port: "8435", }, SyslogSpec: &vmv1.SyslogServerSpec{ TCPListeners: []*vmv1.SyslogTCPListener{ diff --git a/internal/controller/operator/factory/vmagent/vmagent.go b/internal/controller/operator/factory/vmagent/vmagent.go index 8477d84ec..0ac851a47 100644 --- a/internal/controller/operator/factory/vmagent/vmagent.go +++ b/internal/controller/operator/factory/vmagent/vmagent.go @@ -31,14 +31,14 @@ import ( ) const ( - vmAgentConfDir = "/etc/vmagent/config" - vmAgentConfOutDir = "/etc/vmagent/config_out" - vmAgentPersistentQueueDir = "/tmp/vmagent-remotewrite-data" - vmAgentPersistentQueueSTSDir = "/vmagent_pq/vmagent-remotewrite-data" - vmAgentPersistentQueueMountName = "persistent-queue-data" - globalRelabelingName = "global_relabeling.yaml" - urlRelabelingName = "url_relabeling-%d.yaml" - globalAggregationConfigName = "global_aggregation.yaml" + confDir = "/etc/vmagent/config" + confOutDir = "/etc/vmagent/config_out" + persistentQueueDir = "/tmp/vmagent-remotewrite-data" + persistentQueueSTSDir = "/vmagent_pq/vmagent-remotewrite-data" + persistentQueueMountName = "persistent-queue-data" + globalRelabelingName = "global_relabeling.yaml" + urlRelabelingName = "url_relabeling-%d.yaml" + globalAggregationConfigName = "global_aggregation.yaml" tlsAssetsDir = "/etc/vmagent-tls/certs" scrapeGzippedFilename = "vmagent.yaml.gz" @@ -368,8 +368,6 @@ func newK8sApp(cr *vmv1beta1.VMAgent, ac *build.AssetsCache) (client.Object, err return nil, err } - useStrictSecurity := ptr.Deref(cr.Spec.UseStrictSecurity, false) - if cr.Spec.DaemonSetMode { dsSpec := &appsv1.DaemonSet{ ObjectMeta: metav1.ObjectMeta{ @@ -393,8 +391,8 @@ func newK8sApp(cr *vmv1beta1.VMAgent, ac *build.AssetsCache) (client.Object, err }, }, } - build.DaemonSetAddCommonParams(dsSpec, useStrictSecurity, &cr.Spec.CommonApplicationDeploymentParams) - dsSpec.Spec.Template.Spec.Volumes = build.AddServiceAccountTokenVolume(dsSpec.Spec.Template.Spec.Volumes, &cr.Spec.CommonApplicationDeploymentParams) + build.DaemonSetAddCommonParams(dsSpec, &cr.Spec.CommonAppsParams) + dsSpec.Spec.Template.Spec.Volumes = build.AddServiceAccountTokenVolume(dsSpec.Spec.Template.Spec.Volumes, &cr.Spec.CommonAppsParams) return dsSpec, nil } @@ -429,9 +427,9 @@ func newK8sApp(cr *vmv1beta1.VMAgent, ac *build.AssetsCache) (client.Object, err if cr.Spec.PersistentVolumeClaimRetentionPolicy != nil { stsSpec.Spec.PersistentVolumeClaimRetentionPolicy = cr.Spec.PersistentVolumeClaimRetentionPolicy } - build.StatefulSetAddCommonParams(stsSpec, useStrictSecurity, &cr.Spec.CommonApplicationDeploymentParams) - stsSpec.Spec.Template.Spec.Volumes = build.AddServiceAccountTokenVolume(stsSpec.Spec.Template.Spec.Volumes, &cr.Spec.CommonApplicationDeploymentParams) - cr.Spec.StatefulStorage.IntoSTSVolume(vmAgentPersistentQueueMountName, &stsSpec.Spec) + build.StatefulSetAddCommonParams(stsSpec, &cr.Spec.CommonAppsParams) + stsSpec.Spec.Template.Spec.Volumes = build.AddServiceAccountTokenVolume(stsSpec.Spec.Template.Spec.Volumes, &cr.Spec.CommonAppsParams) + cr.Spec.StatefulStorage.IntoSTSVolume(persistentQueueMountName, &stsSpec.Spec) stsSpec.Spec.VolumeClaimTemplates = append(stsSpec.Spec.VolumeClaimTemplates, cr.Spec.ClaimTemplates...) return stsSpec, nil } @@ -465,8 +463,8 @@ func newK8sApp(cr *vmv1beta1.VMAgent, ac *build.AssetsCache) (client.Object, err }, }, } - build.DeploymentAddCommonParams(depSpec, useStrictSecurity, &cr.Spec.CommonApplicationDeploymentParams) - depSpec.Spec.Template.Spec.Volumes = build.AddServiceAccountTokenVolume(depSpec.Spec.Template.Spec.Volumes, &cr.Spec.CommonApplicationDeploymentParams) + build.DeploymentAddCommonParams(depSpec, &cr.Spec.CommonAppsParams) + depSpec.Spec.Template.Spec.Volumes = build.AddServiceAccountTokenVolume(depSpec.Spec.Template.Spec.Volumes, &cr.Spec.CommonAppsParams) return depSpec, nil } @@ -532,13 +530,13 @@ func newPodSpec(cr *vmv1beta1.VMAgent, ac *build.AssetsCache) (*corev1.PodSpec, var crMounts []corev1.VolumeMount // mount data path any way, even if user changes its value // we cannot rely on value of remoteWriteSettings. - pqMountPath := vmAgentPersistentQueueDir + pqMountPath := persistentQueueDir if cr.Spec.StatefulMode { - pqMountPath = vmAgentPersistentQueueSTSDir + pqMountPath = persistentQueueSTSDir } agentVolumeMounts = append(agentVolumeMounts, corev1.VolumeMount{ - Name: vmAgentPersistentQueueMountName, + Name: persistentQueueMountName, MountPath: pqMountPath, }, ) @@ -548,7 +546,7 @@ func newPodSpec(cr *vmv1beta1.VMAgent, ac *build.AssetsCache) (*corev1.PodSpec, // in case for sts, we have to use persistentVolumeClaimTemplate instead if !cr.Spec.StatefulMode { volumes = append(volumes, corev1.Volume{ - Name: vmAgentPersistentQueueMountName, + Name: persistentQueueMountName, VolumeSource: corev1.VolumeSource{ EmptyDir: &corev1.EmptyDirVolumeSource{}, }, @@ -559,7 +557,7 @@ func newPodSpec(cr *vmv1beta1.VMAgent, ac *build.AssetsCache) (*corev1.PodSpec, if !ptr.Deref(cr.Spec.IngestOnlyMode, false) { args = append(args, - fmt.Sprintf("-promscrape.config=%s", path.Join(vmAgentConfOutDir, configFilename))) + fmt.Sprintf("-promscrape.config=%s", path.Join(confOutDir, configFilename))) // preserve order of volumes and volumeMounts // it must prevent vmagent restarts during operator version change @@ -589,7 +587,7 @@ func newPodSpec(cr *vmv1beta1.VMAgent, ac *build.AssetsCache) (*corev1.PodSpec, m := corev1.VolumeMount{ Name: "config-out", - MountPath: vmAgentConfOutDir, + MountPath: confOutDir, } crMounts = append(crMounts, m) m.ReadOnly = true @@ -601,7 +599,7 @@ func newPodSpec(cr *vmv1beta1.VMAgent, ac *build.AssetsCache) (*corev1.PodSpec, }) agentVolumeMounts = append(agentVolumeMounts, corev1.VolumeMount{ Name: string(build.SecretConfigResourceKind), - MountPath: vmAgentConfDir, + MountPath: confDir, ReadOnly: true, }) @@ -675,10 +673,7 @@ func newPodSpec(cr *vmv1beta1.VMAgent, ac *build.AssetsCache) (*corev1.PodSpec, } build.AddServiceAccountTokenVolumeMount(&vmagentContainer, cr.AutomountServiceAccountToken()) - useStrictSecurity := ptr.Deref(cr.Spec.UseStrictSecurity, false) - - vmagentContainer = build.Probe(vmagentContainer, cr) - + build.Probe(&vmagentContainer, cr, &cr.Spec.CommonAppsParams) build.AddConfigReloadAuthKeyToApp(&vmagentContainer, cr.Spec.ExtraArgs, &cr.Spec.CommonConfigReloaderParams) var operatorContainers []corev1.Container @@ -694,7 +689,7 @@ func newPodSpec(cr *vmv1beta1.VMAgent, ac *build.AssetsCache) (*corev1.PodSpec, Key: configFilename, } ic = append(ic, build.ConfigReloaderContainer(true, cr, crMounts, ss)) - build.AddStrictSecuritySettingsToContainers(cr.Spec.SecurityContext, ic, useStrictSecurity) + build.AddStrictSecuritySettingsToContainers(ic, &cr.Spec.CommonAppsParams) } configReloader := build.ConfigReloaderContainer(false, cr, crMounts, ss) operatorContainers = append(operatorContainers, configReloader) @@ -707,7 +702,7 @@ func newPodSpec(cr *vmv1beta1.VMAgent, ac *build.AssetsCache) (*corev1.PodSpec, operatorContainers = append([]corev1.Container{vmagentContainer}, operatorContainers...) - build.AddStrictSecuritySettingsToContainers(cr.Spec.SecurityContext, operatorContainers, useStrictSecurity) + build.AddStrictSecuritySettingsToContainers(operatorContainers, &cr.Spec.CommonAppsParams) containers, err := k8stools.MergePatchContainers(operatorContainers, cr.Spec.Containers) if err != nil { @@ -925,9 +920,9 @@ func buildRemoteWriteArgs(cr *vmv1beta1.VMAgent, ac *build.AssetsCache) ([]strin var hasAnyDiskUsagesSet bool var storageLimit int64 - pqMountPath := vmAgentPersistentQueueDir + pqMountPath := persistentQueueDir if cr.Spec.StatefulMode { - pqMountPath = vmAgentPersistentQueueSTSDir + pqMountPath = persistentQueueSTSDir if cr.Spec.StatefulStorage != nil { if storage, ok := cr.Spec.StatefulStorage.VolumeClaimTemplate.Spec.Resources.Requests[corev1.ResourceStorage]; ok { storageInt, ok := storage.AsInt64() diff --git a/internal/controller/operator/factory/vmagent/vmagent_reconcile_test.go b/internal/controller/operator/factory/vmagent/vmagent_reconcile_test.go index 0929545d7..d54b41d41 100644 --- a/internal/controller/operator/factory/vmagent/vmagent_reconcile_test.go +++ b/internal/controller/operator/factory/vmagent/vmagent_reconcile_test.go @@ -5,13 +5,8 @@ import ( "testing" "github.com/stretchr/testify/assert" - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - rbacv1 "k8s.io/api/rbac/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/utils/ptr" vmv1beta1 "github.com/VictoriaMetrics/operator/api/operator/v1beta1" @@ -21,21 +16,63 @@ import ( func Test_CreateOrUpdate_Actions(t *testing.T) { type args struct { - cr *vmv1beta1.VMAgent - predefinedObjects []runtime.Object + cr *vmv1beta1.VMAgent + preRun func(ctx context.Context, c *k8stools.ClientWithActions, cr *vmv1beta1.VMAgent) } type want struct { actions []k8stools.ClientAction err error } + vmagentName := types.NamespacedName{Namespace: "default", Name: "vmagent-vmagent"} + clusterRoleName := types.NamespacedName{Name: "monitoring:default:vmagent-vmagent"} + tlsAssetsName := types.NamespacedName{Namespace: "default", Name: "tls-assets-vmagent-vmagent"} + + defaultCR := &vmv1beta1.VMAgent{ + ObjectMeta: metav1.ObjectMeta{ + Name: "vmagent", + Namespace: "default", + UID: "123", + }, + Spec: vmv1beta1.VMAgentSpec{ + RemoteWrite: []vmv1beta1.VMAgentRemoteWriteSpec{ + {URL: "http://remote-write"}, + }, + CommonAppsParams: vmv1beta1.CommonAppsParams{ + ReplicaCount: ptr.To(int32(1)), + }, + }, + ParsedLastAppliedSpec: &vmv1beta1.VMAgentSpec{ + RemoteWrite: []vmv1beta1.VMAgentRemoteWriteSpec{ + {URL: "http://remote-write"}, + }, + CommonAppsParams: vmv1beta1.CommonAppsParams{ + ReplicaCount: ptr.To(int32(1)), + }, + }, + } + + crWithoutStatus := defaultCR.DeepCopy() + crWithoutStatus.ParsedLastAppliedSpec = nil + + setupReadyVMAgent := func(ctx context.Context, c *k8stools.ClientWithActions, cr *vmv1beta1.VMAgent) { + // Create objects first + assert.NoError(t, CreateOrUpdate(ctx, cr, c)) + // clear actions + c.Actions = nil + } + f := func(args args, want want) { t.Helper() - // Use shared helper instead of custom interceptors - fclient := k8stools.GetTestClientWithActionsAndObjects(args.predefinedObjects) + fclient := k8stools.GetTestClientWithActionsAndObjects(nil) ctx := context.TODO() build.AddDefaults(fclient.Scheme()) fclient.Scheme().Default(args.cr) + + if args.preRun != nil { + args.preRun(ctx, fclient, args.cr) + } + err := CreateOrUpdate(ctx, args.cr, fclient) if want.err != nil { assert.Error(t, err) @@ -59,10 +96,6 @@ func Test_CreateOrUpdate_Actions(t *testing.T) { } } - vmagentName := types.NamespacedName{Namespace: "default", Name: "vmagent-vmagent"} - clusterRoleName := types.NamespacedName{Name: "monitoring:default:vmagent-vmagent"} - tlsAssetsName := types.NamespacedName{Namespace: "default", Name: "tls-assets-vmagent-vmagent"} - // create vmagent with default config (Deployment mode) f(args{ cr: &vmv1beta1.VMAgent{ @@ -100,6 +133,49 @@ func Test_CreateOrUpdate_Actions(t *testing.T) { }) // update vmagent (Deployment mode) + f(args{ + cr: crWithoutStatus, + preRun: setupReadyVMAgent, + }, + want{ + actions: []k8stools.ClientAction{ + {Verb: "Get", Kind: "ServiceAccount", Resource: vmagentName}, + {Verb: "Get", Kind: "ClusterRole", Resource: clusterRoleName}, + {Verb: "Get", Kind: "ClusterRoleBinding", Resource: clusterRoleName}, + {Verb: "Get", Kind: "Service", Resource: vmagentName}, + {Verb: "Get", Kind: "VMServiceScrape", Resource: vmagentName}, + {Verb: "Get", Kind: "Secret", Resource: vmagentName}, + {Verb: "Get", Kind: "Secret", Resource: tlsAssetsName}, + {Verb: "Get", Kind: "Deployment", Resource: vmagentName}, + {Verb: "Get", Kind: "Deployment", Resource: vmagentName}, + }, + }) + + // no update on status change + f(args{ + cr: defaultCR.DeepCopy(), + preRun: func(ctx context.Context, c *k8stools.ClientWithActions, cr *vmv1beta1.VMAgent) { + setupReadyVMAgent(ctx, c, cr) + // Change status + cr.Status.Replicas = 1 + }, + }, + want{ + actions: []k8stools.ClientAction{ + {Verb: "Get", Kind: "DaemonSet", Resource: vmagentName}, + {Verb: "Get", Kind: "ServiceAccount", Resource: vmagentName}, + {Verb: "Get", Kind: "ClusterRole", Resource: clusterRoleName}, + {Verb: "Get", Kind: "ClusterRoleBinding", Resource: clusterRoleName}, + {Verb: "Get", Kind: "Service", Resource: vmagentName}, + {Verb: "Get", Kind: "VMServiceScrape", Resource: vmagentName}, + {Verb: "Get", Kind: "Secret", Resource: vmagentName}, + {Verb: "Get", Kind: "Secret", Resource: tlsAssetsName}, + {Verb: "Get", Kind: "Deployment", Resource: vmagentName}, + {Verb: "Get", Kind: "Deployment", Resource: vmagentName}, + }, + }) + + // daemonset mode f(args{ cr: &vmv1beta1.VMAgent{ ObjectMeta: metav1.ObjectMeta{ @@ -107,93 +183,32 @@ func Test_CreateOrUpdate_Actions(t *testing.T) { Namespace: "default", }, Spec: vmv1beta1.VMAgentSpec{ + DaemonSetMode: true, RemoteWrite: []vmv1beta1.VMAgentRemoteWriteSpec{ {URL: "http://remote-write"}, }, - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ - ReplicaCount: ptr.To(int32(1)), - }, - }, - }, - predefinedObjects: []runtime.Object{ - &corev1.ServiceAccount{ObjectMeta: metav1.ObjectMeta{Name: vmagentName.Name, Namespace: vmagentName.Namespace}}, - &rbacv1.ClusterRole{ObjectMeta: metav1.ObjectMeta{Name: clusterRoleName.Name}}, - &rbacv1.ClusterRoleBinding{ObjectMeta: metav1.ObjectMeta{Name: clusterRoleName.Name}}, - &corev1.Service{ - ObjectMeta: metav1.ObjectMeta{Name: vmagentName.Name, Namespace: vmagentName.Namespace}, - Spec: corev1.ServiceSpec{ - Type: corev1.ServiceTypeClusterIP, - ClusterIP: "10.0.0.1", - Selector: map[string]string{ - "app.kubernetes.io/name": "vmagent", - "app.kubernetes.io/instance": "vmagent", - "app.kubernetes.io/component": "monitoring", - "managed-by": "vm-operator", - }, - Ports: []corev1.ServicePort{ - { - Name: "http", - Protocol: "TCP", - Port: 8429, - TargetPort: intstr.Parse("8429"), - }, - }, - }, - }, - &vmv1beta1.VMServiceScrape{ObjectMeta: metav1.ObjectMeta{Name: vmagentName.Name, Namespace: vmagentName.Namespace}}, - &corev1.Secret{ObjectMeta: metav1.ObjectMeta{Name: vmagentName.Name, Namespace: vmagentName.Namespace}}, - &corev1.Secret{ObjectMeta: metav1.ObjectMeta{Name: tlsAssetsName.Name, Namespace: tlsAssetsName.Namespace}}, - &appsv1.Deployment{ - ObjectMeta: metav1.ObjectMeta{Name: vmagentName.Name, Namespace: vmagentName.Namespace}, - Spec: appsv1.DeploymentSpec{ - Replicas: ptr.To(int32(1)), - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - "app.kubernetes.io/name": "vmagent", - "app.kubernetes.io/instance": "vmagent", - "app.kubernetes.io/component": "monitoring", - "managed-by": "vm-operator", - }, - }, - Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{ - "app.kubernetes.io/name": "vmagent", - "app.kubernetes.io/instance": "vmagent", - "app.kubernetes.io/component": "monitoring", - "managed-by": "vm-operator", - }, - }, - }, - }, - Status: appsv1.DeploymentStatus{ - Replicas: 1, - ReadyReplicas: 1, - UpdatedReplicas: 1, - ObservedGeneration: 1, - }, }, }, }, want{ actions: []k8stools.ClientAction{ {Verb: "Get", Kind: "ServiceAccount", Resource: vmagentName}, - {Verb: "Update", Kind: "ServiceAccount", Resource: vmagentName}, + {Verb: "Create", Kind: "ServiceAccount", Resource: vmagentName}, {Verb: "Get", Kind: "ClusterRole", Resource: clusterRoleName}, - {Verb: "Update", Kind: "ClusterRole", Resource: clusterRoleName}, + {Verb: "Create", Kind: "ClusterRole", Resource: clusterRoleName}, {Verb: "Get", Kind: "ClusterRoleBinding", Resource: clusterRoleName}, - {Verb: "Update", Kind: "ClusterRoleBinding", Resource: clusterRoleName}, + {Verb: "Create", Kind: "ClusterRoleBinding", Resource: clusterRoleName}, {Verb: "Get", Kind: "Service", Resource: vmagentName}, - {Verb: "Update", Kind: "Service", Resource: vmagentName}, - {Verb: "Get", Kind: "VMServiceScrape", Resource: vmagentName}, - {Verb: "Update", Kind: "VMServiceScrape", Resource: vmagentName}, + {Verb: "Create", Kind: "Service", Resource: vmagentName}, + {Verb: "Get", Kind: "VMPodScrape", Resource: vmagentName}, + {Verb: "Create", Kind: "VMPodScrape", Resource: vmagentName}, {Verb: "Get", Kind: "Secret", Resource: vmagentName}, - {Verb: "Update", Kind: "Secret", Resource: vmagentName}, + {Verb: "Create", Kind: "Secret", Resource: vmagentName}, {Verb: "Get", Kind: "Secret", Resource: tlsAssetsName}, - {Verb: "Update", Kind: "Secret", Resource: tlsAssetsName}, - {Verb: "Get", Kind: "Deployment", Resource: vmagentName}, - {Verb: "Update", Kind: "Deployment", Resource: vmagentName}, - {Verb: "Get", Kind: "Deployment", Resource: vmagentName}, + {Verb: "Create", Kind: "Secret", Resource: tlsAssetsName}, + {Verb: "Get", Kind: "DaemonSet", Resource: vmagentName}, + {Verb: "Create", Kind: "DaemonSet", Resource: vmagentName}, + {Verb: "Get", Kind: "DaemonSet", Resource: vmagentName}, }, }) } diff --git a/internal/controller/operator/factory/vmagent/vmagent_scrapeconfig.go b/internal/controller/operator/factory/vmagent/vmagent_scrapeconfig.go index 1686f271e..4f66bba4b 100644 --- a/internal/controller/operator/factory/vmagent/vmagent_scrapeconfig.go +++ b/internal/controller/operator/factory/vmagent/vmagent_scrapeconfig.go @@ -711,7 +711,7 @@ func addEndpointAuthTo(cfg yaml.MapSlice, ea *vmv1beta1.EndpointAuth, namespace func getAssetsCache(ctx context.Context, rclient client.Client, cr *vmv1beta1.VMAgent) *build.AssetsCache { cfg := map[build.ResourceKind]*build.ResourceCfg{ build.SecretConfigResourceKind: { - MountDir: vmAgentConfDir, + MountDir: confDir, SecretName: build.ResourceName(build.SecretConfigResourceKind, cr), }, build.TLSAssetsResourceKind: { diff --git a/internal/controller/operator/factory/vmagent/vmagent_test.go b/internal/controller/operator/factory/vmagent/vmagent_test.go index ca9d59953..94311fec1 100644 --- a/internal/controller/operator/factory/vmagent/vmagent_test.go +++ b/internal/controller/operator/factory/vmagent/vmagent_test.go @@ -62,11 +62,10 @@ func TestCreateOrUpdate(t *testing.T) { RemoteWrite: []vmv1beta1.VMAgentRemoteWriteSpec{ {URL: "http://remote-write"}, }, - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), }, - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{}, - StatefulMode: true, + StatefulMode: true, CommonScrapeParams: vmv1beta1.CommonScrapeParams{ IngestOnlyMode: ptr.To(true), }, @@ -129,7 +128,7 @@ func TestCreateOrUpdate(t *testing.T) { Namespace: "default", }, Spec: vmv1beta1.VMAgentSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), }, RemoteWrite: []vmv1beta1.VMAgentRemoteWriteSpec{ @@ -414,7 +413,7 @@ func TestCreateOrUpdate(t *testing.T) { RemoteWrite: []vmv1beta1.VMAgentRemoteWriteSpec{ {URL: "http://remote-write"}, }, - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), }, StatefulMode: true, @@ -453,7 +452,7 @@ func TestCreateOrUpdate(t *testing.T) { CommonScrapeParams: vmv1beta1.CommonScrapeParams{ IngestOnlyMode: ptr.To(true), }, - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](2), }, ShardCount: ptr.To(3), @@ -519,7 +518,7 @@ func TestCreateOrUpdate(t *testing.T) { RemoteWrite: []vmv1beta1.VMAgentRemoteWriteSpec{ {URL: "http://remote-write"}, }, - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), }, StatefulMode: true, @@ -582,7 +581,7 @@ func TestCreateOrUpdate(t *testing.T) { Namespace: "default", }, Spec: vmv1beta1.VMAgentSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(0)), }, StatefulMode: true, @@ -2077,7 +2076,7 @@ func TestMakeSpecForAgentOk(t *testing.T) { CommonScrapeParams: vmv1beta1.CommonScrapeParams{ IngestOnlyMode: ptr.To(true), }, - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ Image: vmv1beta1.Image{ Repository: "vm-repo", Tag: "v1.97.1", @@ -2165,7 +2164,7 @@ serviceaccountname: vmagent-agent`, CommonScrapeParams: vmv1beta1.CommonScrapeParams{ IngestOnlyMode: ptr.To(true), }, - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ Image: vmv1beta1.Image{ Repository: "vm-repo", Tag: "v1.97.1", @@ -2318,7 +2317,7 @@ serviceaccountname: vmagent-agent`, CommonScrapeParams: vmv1beta1.CommonScrapeParams{ IngestOnlyMode: ptr.To(false), }, - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ Image: vmv1beta1.Image{ Tag: "v1.97.1", }, @@ -2459,7 +2458,7 @@ serviceaccountname: vmagent-agent CommonScrapeParams: vmv1beta1.CommonScrapeParams{ IngestOnlyMode: ptr.To(true), }, - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ Image: vmv1beta1.Image{ Tag: "v1.97.1", }, @@ -2542,7 +2541,7 @@ serviceaccountname: vmagent-agent CommonScrapeParams: vmv1beta1.CommonScrapeParams{ IngestOnlyMode: ptr.To(true), }, - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ Image: vmv1beta1.Image{ Tag: "v1.97.1", }, @@ -2628,22 +2627,20 @@ serviceaccountname: vmagent-agent CommonScrapeParams: vmv1beta1.CommonScrapeParams{ IngestOnlyMode: ptr.To(true), }, - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ Image: vmv1beta1.Image{ Tag: "v1.97.1", }, UseDefaultResources: ptr.To(false), Port: "8425", - }, - CommonConfigReloaderParams: vmv1beta1.CommonConfigReloaderParams{ - ConfigReloaderImage: "vmcustom:config-reloader-v0.35.0", - }, - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ ExtraArgs: map[string]string{ "remoteWrite.maxDiskUsagePerURL": "35GiB", "remoteWrite.forceVMProto": "false", }, }, + CommonConfigReloaderParams: vmv1beta1.CommonConfigReloaderParams{ + ConfigReloaderImage: "vmcustom:config-reloader-v0.35.0", + }, RemoteWrite: []vmv1beta1.VMAgentRemoteWriteSpec{ { URL: "http://some-url/api/v1/write", diff --git a/internal/controller/operator/factory/vmalert/vmalert.go b/internal/controller/operator/factory/vmalert/vmalert.go index e71ed22e6..b056bee79 100644 --- a/internal/controller/operator/factory/vmalert/vmalert.go +++ b/internal/controller/operator/factory/vmalert/vmalert.go @@ -175,7 +175,7 @@ func newDeploy(cr *vmv1beta1.VMAlert, ruleConfigMapNames []string, ac *build.Ass }, Spec: *generatedSpec, } - build.DeploymentAddCommonParams(deploy, ptr.Deref(cr.Spec.UseStrictSecurity, false), &cr.Spec.CommonApplicationDeploymentParams) + build.DeploymentAddCommonParams(deploy, &cr.Spec.CommonAppsParams) return deploy, nil } @@ -298,7 +298,7 @@ func newPodSpec(cr *vmv1beta1.VMAlert, ruleConfigMapNames []string, ac *build.As EnvFrom: cr.Spec.ExtraEnvsFrom, TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, } - vmalertContainer = build.Probe(vmalertContainer, cr) + build.Probe(&vmalertContainer, cr, &cr.Spec.CommonAppsParams) build.AddConfigReloadAuthKeyToApp(&vmalertContainer, cr.Spec.ExtraArgs, &cr.Spec.CommonConfigReloaderParams) vmalertContainers = append(vmalertContainers, vmalertContainer) @@ -307,9 +307,7 @@ func newPodSpec(cr *vmv1beta1.VMAlert, ruleConfigMapNames []string, ac *build.As vmalertContainers = append(vmalertContainers, crc) } - useStrictSecurity := ptr.Deref(cr.Spec.UseStrictSecurity, false) - - build.AddStrictSecuritySettingsToContainers(cr.Spec.SecurityContext, vmalertContainers, useStrictSecurity) + build.AddStrictSecuritySettingsToContainers(vmalertContainers, &cr.Spec.CommonAppsParams) containers, err := k8stools.MergePatchContainers(vmalertContainers, cr.Spec.Containers) if err != nil { return nil, err @@ -522,8 +520,8 @@ func buildArgs(cr *vmv1beta1.VMAlert, ruleConfigMapNames []string, ac *build.Ass } args = build.LicenseArgsTo(args, cr.Spec.License, vmv1beta1.SecretsDir) - args = build.AddExtraArgsOverrideDefaults(args, cr.Spec.ExtraArgs, "-") + sort.Strings(args) return args, nil } diff --git a/internal/controller/operator/factory/vmalert/vmalert_reconcile_test.go b/internal/controller/operator/factory/vmalert/vmalert_reconcile_test.go index 4cfbf627a..700af290d 100644 --- a/internal/controller/operator/factory/vmalert/vmalert_reconcile_test.go +++ b/internal/controller/operator/factory/vmalert/vmalert_reconcile_test.go @@ -5,15 +5,9 @@ import ( "testing" "github.com/stretchr/testify/assert" - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/client-go/kubernetes/scheme" "k8s.io/utils/ptr" - "sigs.k8s.io/controller-runtime/pkg/client/fake" vmv1beta1 "github.com/VictoriaMetrics/operator/api/operator/v1beta1" "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/build" @@ -21,9 +15,10 @@ import ( ) func Test_CreateOrUpdate_Actions(t *testing.T) { + type args struct { - cr *vmv1beta1.VMAlert - predefinedObjects []runtime.Object + cr *vmv1beta1.VMAlert + preRun func(ctx context.Context, c *k8stools.ClientWithActions, cr *vmv1beta1.VMAlert) } type want struct { actions []k8stools.ClientAction @@ -33,25 +28,15 @@ func Test_CreateOrUpdate_Actions(t *testing.T) { f := func(args args, want want) { t.Helper() - // Use local scheme to avoid global scheme pollution - s := runtime.NewScheme() - _ = scheme.AddToScheme(s) - _ = vmv1beta1.AddToScheme(s) - build.AddDefaults(s) - s.Default(args.cr) - - var actions []k8stools.ClientAction - objInterceptors := k8stools.GetInterceptorsWithObjects() - actionInterceptor := k8stools.NewActionRecordingInterceptor(&actions, &objInterceptors) + fclient := k8stools.GetTestClientWithActionsAndObjects(nil) + ctx := context.TODO() + build.AddDefaults(fclient.Scheme()) + fclient.Scheme().Default(args.cr) - fclient := fake.NewClientBuilder(). - WithScheme(s). - WithStatusSubresource(&vmv1beta1.VMAlert{}). - WithRuntimeObjects(args.predefinedObjects...). - WithInterceptorFuncs(actionInterceptor). - Build() + if args.preRun != nil { + args.preRun(ctx, fclient, args.cr) + } - ctx := context.TODO() err := CreateOrUpdate(ctx, args.cr, fclient, nil) if want.err != nil { assert.Error(t, err) @@ -59,36 +44,45 @@ func Test_CreateOrUpdate_Actions(t *testing.T) { assert.NoError(t, err) } - if !assert.Equal(t, len(want.actions), len(actions)) { - for i, action := range actions { + if !assert.Equal(t, len(want.actions), len(fclient.Actions)) { + for i, action := range fclient.Actions { t.Logf("Action %d: %s %s %s", i, action.Verb, action.Kind, action.Resource) } } for i, action := range want.actions { - if i >= len(actions) { + if i >= len(fclient.Actions) { break } - assert.Equal(t, action.Verb, actions[i].Verb, "idx %d verb", i) - assert.Equal(t, action.Kind, actions[i].Kind, "idx %d kind", i) - assert.Equal(t, action.Resource, actions[i].Resource, "idx %d resource", i) + assert.Equal(t, action.Verb, fclient.Actions[i].Verb, "idx %d verb", i) + assert.Equal(t, action.Kind, fclient.Actions[i].Kind, "idx %d kind", i) + assert.Equal(t, action.Resource, fclient.Actions[i].Resource, "idx %d resource", i) } } vmalertName := types.NamespacedName{Namespace: "default", Name: "vmalert-vmalert"} - vmalertMeta := metav1.ObjectMeta{Name: "vmalert-vmalert", Namespace: "default"} tlsAssetsName := types.NamespacedName{Namespace: "default", Name: "tls-assets-vmalert-vmalert"} objectMeta := metav1.ObjectMeta{Name: "vmalert", Namespace: "default"} + defaultCR := &vmv1beta1.VMAlert{ + ObjectMeta: objectMeta, + Spec: vmv1beta1.VMAlertSpec{ + Datasource: vmv1beta1.VMAlertDatasourceSpec{URL: "http://datasource"}, + Notifier: &vmv1beta1.VMAlertNotifierSpec{URL: "http://notifier"}, + }, + } + + setupReadyVMAlert := func(ctx context.Context, c *k8stools.ClientWithActions, cr *vmv1beta1.VMAlert) { + // Create the object first + assert.NoError(t, CreateOrUpdate(ctx, cr.DeepCopy(), c, nil)) + + // clear actions + c.Actions = nil + } + // create vmalert with default config f(args{ - cr: &vmv1beta1.VMAlert{ - ObjectMeta: objectMeta, - Spec: vmv1beta1.VMAlertSpec{ - Datasource: vmv1beta1.VMAlertDatasourceSpec{URL: "http://datasource"}, - Notifier: &vmv1beta1.VMAlertNotifierSpec{URL: "http://notifier"}, - }, - }, + cr: defaultCR.DeepCopy(), }, want{ actions: []k8stools.ClientAction{ @@ -110,93 +104,62 @@ func Test_CreateOrUpdate_Actions(t *testing.T) { }, }) - // update vmalert + // update vmalert with no changes f(args{ cr: &vmv1beta1.VMAlert{ ObjectMeta: objectMeta, Spec: vmv1beta1.VMAlertSpec{ Datasource: vmv1beta1.VMAlertDatasourceSpec{URL: "http://datasource"}, Notifier: &vmv1beta1.VMAlertNotifierSpec{URL: "http://notifier"}, - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), }, }, }, - predefinedObjects: []runtime.Object{ - &corev1.ServiceAccount{ObjectMeta: vmalertMeta}, - &corev1.Service{ - ObjectMeta: vmalertMeta, - Spec: corev1.ServiceSpec{ - Type: corev1.ServiceTypeClusterIP, - ClusterIP: "10.0.0.1", - Selector: map[string]string{ - "app.kubernetes.io/name": "vmalert", - "app.kubernetes.io/instance": "vmalert", - "app.kubernetes.io/component": "monitoring", - "managed-by": "vm-operator", - }, - Ports: []corev1.ServicePort{ - { - Name: "http", - Protocol: "TCP", - Port: 8880, - TargetPort: intstr.Parse("8880"), - }, - }, - }, - }, - &vmv1beta1.VMServiceScrape{ObjectMeta: vmalertMeta}, - &corev1.Secret{ObjectMeta: vmalertMeta}, - &corev1.Secret{ObjectMeta: metav1.ObjectMeta{Name: "tls-assets-vmalert-vmalert", Namespace: "default"}}, - &appsv1.Deployment{ - ObjectMeta: vmalertMeta, - Spec: appsv1.DeploymentSpec{ - Replicas: ptr.To(int32(1)), - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - "app.kubernetes.io/name": "vmalert", - "app.kubernetes.io/instance": "vmalert", - "app.kubernetes.io/component": "monitoring", - "managed-by": "vm-operator", - }, - }, - Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{ - "app.kubernetes.io/name": "vmalert", - "app.kubernetes.io/instance": "vmalert", - "app.kubernetes.io/component": "monitoring", - "managed-by": "vm-operator", - }, - }, - }, - }, - Status: appsv1.DeploymentStatus{ - Replicas: 1, - ReadyReplicas: 1, - UpdatedReplicas: 1, - ObservedGeneration: 1, - }, - }, - }, + preRun: setupReadyVMAlert, }, want{ actions: []k8stools.ClientAction{ {Verb: "Get", Kind: "ServiceAccount", Resource: vmalertName}, - {Verb: "Update", Kind: "ServiceAccount", Resource: vmalertName}, {Verb: "Get", Kind: "Service", Resource: vmalertName}, - {Verb: "Update", Kind: "Service", Resource: vmalertName}, {Verb: "Get", Kind: "VMServiceScrape", Resource: vmalertName}, - {Verb: "Update", Kind: "VMServiceScrape", Resource: vmalertName}, // Secrets {Verb: "Get", Kind: "Secret", Resource: vmalertName}, - {Verb: "Update", Kind: "Secret", Resource: vmalertName}, {Verb: "Get", Kind: "Secret", Resource: tlsAssetsName}, - {Verb: "Update", Kind: "Secret", Resource: tlsAssetsName}, // Deployment {Verb: "Get", Kind: "Deployment", Resource: vmalertName}, - {Verb: "Update", Kind: "Deployment", Resource: vmalertName}, {Verb: "Get", Kind: "Deployment", Resource: vmalertName}, }, }) + + // no update on status change + f(args{ + cr: &vmv1beta1.VMAlert{ + ObjectMeta: objectMeta, + Spec: vmv1beta1.VMAlertSpec{ + Datasource: vmv1beta1.VMAlertDatasourceSpec{URL: "http://datasource"}, + Notifier: &vmv1beta1.VMAlertNotifierSpec{URL: "http://notifier"}, + CommonAppsParams: vmv1beta1.CommonAppsParams{ + ReplicaCount: ptr.To(int32(1)), + }, + }, + }, + preRun: func(ctx context.Context, c *k8stools.ClientWithActions, cr *vmv1beta1.VMAlert) { + setupReadyVMAlert(ctx, c, cr) + + // Update status to simulate consistency + cr.ParsedLastAppliedSpec = cr.Spec.DeepCopy() + }, + }, want{ + actions: []k8stools.ClientAction{ + {Verb: "Get", Kind: "PodDisruptionBudget", Resource: vmalertName}, + {Verb: "Get", Kind: "ServiceAccount", Resource: vmalertName}, + {Verb: "Get", Kind: "Service", Resource: vmalertName}, + {Verb: "Get", Kind: "VMServiceScrape", Resource: vmalertName}, + {Verb: "Get", Kind: "Secret", Resource: vmalertName}, + {Verb: "Get", Kind: "Secret", Resource: tlsAssetsName}, + {Verb: "Get", Kind: "Deployment", Resource: vmalertName}, + {Verb: "Get", Kind: "Deployment", Resource: vmalertName}, + }, + }) } diff --git a/internal/controller/operator/factory/vmalert/vmalert_test.go b/internal/controller/operator/factory/vmalert/vmalert_test.go index d23c71f91..504ed3892 100644 --- a/internal/controller/operator/factory/vmalert/vmalert_test.go +++ b/internal/controller/operator/factory/vmalert/vmalert_test.go @@ -656,6 +656,8 @@ func Test_buildVMAlertArgs(t *testing.T) { t.Helper() ctx := context.Background() fclient := k8stools.GetTestClientWithObjects(o.predefinedObjects) + build.AddDefaults(fclient.Scheme()) + fclient.Scheme().Default(o.cr) ac := getAssetsCache(ctx, fclient, o.cr) assert.NoError(t, discoverNotifiersIfNeeded(ctx, fclient, o.cr)) got, err := buildArgs(o.cr, o.ruleConfigMapNames, ac) @@ -674,7 +676,7 @@ func Test_buildVMAlertArgs(t *testing.T) { Datasource: vmv1beta1.VMAlertDatasourceSpec{ URL: "http://vmsingle-url", }, - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ExtraArgs: map[string]string{ "notifier.url": "http://test", }, @@ -682,7 +684,7 @@ func Test_buildVMAlertArgs(t *testing.T) { }, }, ruleConfigMapNames: []string{"first-rule-cm.yaml"}, - want: []string{"-datasource.url=http://vmsingle-url", "-httpListenAddr=:", "-notifier.url=http://test", "-rule=\"/etc/vmalert/config/first-rule-cm.yaml/*.yaml\""}, + want: []string{"-datasource.url=http://vmsingle-url", "-httpListenAddr=:8080", "-notifier.url=http://test", "-rule=\"/etc/vmalert/config/first-rule-cm.yaml/*.yaml\""}, }) // with tls args @@ -704,7 +706,7 @@ func Test_buildVMAlertArgs(t *testing.T) { }, }, }, - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ExtraArgs: map[string]string{ "notifier.url": "http://test", }, @@ -712,7 +714,7 @@ func Test_buildVMAlertArgs(t *testing.T) { }, }, ruleConfigMapNames: []string{"first-rule-cm.yaml"}, - want: []string{"--datasource.headers=x-org-id:one^^x-org-tenant:5", "-datasource.tlsCAFile=/path/to/sa", "-datasource.tlsInsecureSkipVerify=true", "-datasource.tlsKeyFile=/path/to/key", "-datasource.url=http://vmsingle-url", "-httpListenAddr=:", "-notifier.url=http://test", "-rule=\"/etc/vmalert/config/first-rule-cm.yaml/*.yaml\""}, + want: []string{"--datasource.headers=x-org-id:one^^x-org-tenant:5", "-datasource.tlsCAFile=/path/to/sa", "-datasource.tlsInsecureSkipVerify=true", "-datasource.tlsKeyFile=/path/to/key", "-datasource.url=http://vmsingle-url", "-httpListenAddr=:8080", "-notifier.url=http://test", "-rule=\"/etc/vmalert/config/first-rule-cm.yaml/*.yaml\""}, }) // with static and selector notifiers @@ -765,7 +767,7 @@ func Test_buildVMAlertArgs(t *testing.T) { Labels: map[string]string{"main": "system"}, }, Spec: vmv1beta1.VMAlertmanagerSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), }, }, @@ -778,7 +780,7 @@ func Test_buildVMAlertArgs(t *testing.T) { Labels: map[string]string{"main": "system"}, }, Spec: vmv1beta1.VMAlertmanagerSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), }, }, @@ -786,7 +788,7 @@ func Test_buildVMAlertArgs(t *testing.T) { }, want: []string{ "-datasource.url=http://some-vm-datasource", - "-httpListenAddr=:", + "-httpListenAddr=:8080", "-notifier.tlsCAFile=,/tmp/ca.cert,,,", "-notifier.tlsCertFile=,/tmp/cert.pem,,,", "-notifier.tlsInsecureSkipVerify=false,true,false,false,false", diff --git a/internal/controller/operator/factory/vmalertmanager/alertmanager_test.go b/internal/controller/operator/factory/vmalertmanager/alertmanager_test.go index 6880ad2f0..9f077689e 100644 --- a/internal/controller/operator/factory/vmalertmanager/alertmanager_test.go +++ b/internal/controller/operator/factory/vmalertmanager/alertmanager_test.go @@ -58,7 +58,7 @@ func TestCreateOrUpdateAlertManager(t *testing.T) { Annotations: map[string]string{"not": "touch"}, Labels: map[string]string{"main": "system"}, }, - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), }, }, @@ -95,10 +95,8 @@ func TestCreateOrUpdateAlertManager(t *testing.T) { Labels: map[string]string{"main": "system"}, }, Spec: vmv1beta1.VMAlertmanagerSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), - }, - EmbeddedProbes: &vmv1beta1.EmbeddedProbes{ LivenessProbe: &corev1.Probe{ TimeoutSeconds: 20, }, @@ -176,7 +174,7 @@ func TestCreateOrUpdateAlertManager(t *testing.T) { Labels: map[string]string{"main": "system"}, }, Spec: vmv1beta1.VMAlertmanagerSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(3)), }, }, @@ -211,7 +209,7 @@ func TestCreateOrUpdateAlertManager(t *testing.T) { }, Spec: vmv1beta1.VMAlertmanagerSpec{ ClusterDomainName: "example.com", - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(3)), }, }, diff --git a/internal/controller/operator/factory/vmalertmanager/statefulset.go b/internal/controller/operator/factory/vmalertmanager/statefulset.go index 10cf0a1ef..f2118aaad 100644 --- a/internal/controller/operator/factory/vmalertmanager/statefulset.go +++ b/internal/controller/operator/factory/vmalertmanager/statefulset.go @@ -73,8 +73,7 @@ func newStsForAlertManager(cr *vmv1beta1.VMAlertmanager) (*appsv1.StatefulSet, e statefulset.Spec.PersistentVolumeClaimRetentionPolicy = cr.Spec.PersistentVolumeClaimRetentionPolicy } - cfg := config.MustGetBaseConfig() - build.StatefulSetAddCommonParams(statefulset, ptr.Deref(cr.Spec.UseStrictSecurity, cfg.EnableStrictSecurity), &cr.Spec.CommonApplicationDeploymentParams) + build.StatefulSetAddCommonParams(statefulset, &cr.Spec.CommonAppsParams) cr.Spec.Storage.IntoSTSVolume(cr.GetVolumeName(), &statefulset.Spec) statefulset.Spec.Template.Spec.Volumes = append(statefulset.Spec.Template.Spec.Volumes, cr.Spec.Volumes...) @@ -155,7 +154,6 @@ func createOrUpdateAlertManagerService(ctx context.Context, rclient client.Clien func makeStatefulSetSpec(cr *vmv1beta1.VMAlertmanager) (*appsv1.StatefulSetSpec, error) { - cfg := config.MustGetBaseConfig() image := fmt.Sprintf("%s:%s", cr.Spec.Image.Repository, cr.Spec.Image.Tag) amArgs := []string{ @@ -377,8 +375,6 @@ func makeStatefulSetSpec(cr *vmv1beta1.VMAlertmanager) (*appsv1.StatefulSetSpec, var initContainers []corev1.Container - useStrictSecurity := ptr.Deref(cr.Spec.UseStrictSecurity, cfg.EnableStrictSecurity) - ss := &corev1.SecretKeySelector{ LocalObjectReference: corev1.LocalObjectReference{ Name: cr.ConfigSecretName(), @@ -386,7 +382,7 @@ func makeStatefulSetSpec(cr *vmv1beta1.VMAlertmanager) (*appsv1.StatefulSetSpec, Key: alertmanagerSecretConfigKey, } initContainers = append(initContainers, build.ConfigReloaderContainer(true, cr, crMounts, ss)) - build.AddStrictSecuritySettingsToContainers(cr.Spec.SecurityContext, initContainers, useStrictSecurity) + build.AddStrictSecuritySettingsToContainers(initContainers, &cr.Spec.CommonAppsParams) ic, err := k8stools.MergePatchContainers(initContainers, cr.Spec.InitContainers) if err != nil { @@ -404,11 +400,11 @@ func makeStatefulSetSpec(cr *vmv1beta1.VMAlertmanager) (*appsv1.StatefulSetSpec, Env: envs, TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, } - vmaContainer = build.Probe(vmaContainer, cr) + build.Probe(&vmaContainer, cr, &cr.Spec.CommonAppsParams) operatorContainers := []corev1.Container{vmaContainer} operatorContainers = append(operatorContainers, build.ConfigReloaderContainer(false, cr, crMounts, ss)) - build.AddStrictSecuritySettingsToContainers(cr.Spec.SecurityContext, operatorContainers, useStrictSecurity) + build.AddStrictSecuritySettingsToContainers(operatorContainers, &cr.Spec.CommonAppsParams) containers, err := k8stools.MergePatchContainers(operatorContainers, cr.Spec.Containers) if err != nil { return nil, fmt.Errorf("failed to merge containers spec: %w", err) @@ -421,7 +417,7 @@ func makeStatefulSetSpec(cr *vmv1beta1.VMAlertmanager) (*appsv1.StatefulSetSpec, } } } - volumes = build.AddServiceAccountTokenVolume(volumes, &cr.Spec.CommonApplicationDeploymentParams) + volumes = build.AddServiceAccountTokenVolume(volumes, &cr.Spec.CommonAppsParams) return &appsv1.StatefulSetSpec{ ServiceName: cr.PrefixedName(), UpdateStrategy: appsv1.StatefulSetUpdateStrategy{ diff --git a/internal/controller/operator/factory/vmalertmanager/vmalertmanager_reconcile_test.go b/internal/controller/operator/factory/vmalertmanager/vmalertmanager_reconcile_test.go index 101d887d6..d5c499260 100644 --- a/internal/controller/operator/factory/vmalertmanager/vmalertmanager_reconcile_test.go +++ b/internal/controller/operator/factory/vmalertmanager/vmalertmanager_reconcile_test.go @@ -7,14 +7,9 @@ import ( "github.com/stretchr/testify/assert" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" - rbacv1 "k8s.io/api/rbac/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/client-go/kubernetes/scheme" "k8s.io/utils/ptr" - "sigs.k8s.io/controller-runtime/pkg/client/fake" vmv1beta1 "github.com/VictoriaMetrics/operator/api/operator/v1beta1" "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/build" @@ -23,8 +18,8 @@ import ( func Test_CreateOrUpdate_Actions(t *testing.T) { type args struct { - cr *vmv1beta1.VMAlertmanager - predefinedObjects []runtime.Object + cr *vmv1beta1.VMAlertmanager + preRun func(ctx context.Context, c *k8stools.ClientWithActions, cr *vmv1beta1.VMAlertmanager) } type want struct { actions []k8stools.ClientAction @@ -34,25 +29,15 @@ func Test_CreateOrUpdate_Actions(t *testing.T) { f := func(args args, want want) { t.Helper() - // Use local scheme to avoid global scheme pollution - s := runtime.NewScheme() - _ = scheme.AddToScheme(s) - _ = vmv1beta1.AddToScheme(s) - build.AddDefaults(s) - s.Default(args.cr) - - var actions []k8stools.ClientAction - objInterceptors := k8stools.GetInterceptorsWithObjects() - actionInterceptor := k8stools.NewActionRecordingInterceptor(&actions, &objInterceptors) + fclient := k8stools.GetTestClientWithActionsAndObjects(nil) + ctx := context.TODO() + build.AddDefaults(fclient.Scheme()) + fclient.Scheme().Default(args.cr) - fclient := fake.NewClientBuilder(). - WithScheme(s). - WithStatusSubresource(&vmv1beta1.VMAlertmanager{}). - WithRuntimeObjects(args.predefinedObjects...). - WithInterceptorFuncs(actionInterceptor). - Build() + if args.preRun != nil { + args.preRun(ctx, fclient, args.cr) + } - ctx := context.TODO() err := CreateOrUpdateAlertManager(ctx, args.cr, fclient) if want.err != nil { assert.Error(t, err) @@ -60,19 +45,19 @@ func Test_CreateOrUpdate_Actions(t *testing.T) { assert.NoError(t, err) } - if !assert.Equal(t, len(want.actions), len(actions)) { - for i, action := range actions { + if !assert.Equal(t, len(want.actions), len(fclient.Actions)) { + for i, action := range fclient.Actions { t.Logf("Action %d: %s %s %s", i, action.Verb, action.Kind, action.Resource) } } for i, action := range want.actions { - if i >= len(actions) { + if i >= len(fclient.Actions) { break } - assert.Equal(t, action.Verb, actions[i].Verb, "idx %d verb", i) - assert.Equal(t, action.Kind, actions[i].Kind, "idx %d kind", i) - assert.Equal(t, action.Resource, actions[i].Resource, "idx %d resource", i) + assert.Equal(t, action.Verb, fclient.Actions[i].Verb, "idx %d verb", i) + assert.Equal(t, action.Kind, fclient.Actions[i].Kind, "idx %d kind", i) + assert.Equal(t, action.Resource, fclient.Actions[i].Resource, "idx %d resource", i) } } @@ -80,7 +65,55 @@ func Test_CreateOrUpdate_Actions(t *testing.T) { namespace := "default" vmalertmanagerName := types.NamespacedName{Namespace: namespace, Name: "vmalertmanager-" + name} objectMeta := metav1.ObjectMeta{Name: name, Namespace: namespace} - childObjectMeta := metav1.ObjectMeta{Name: vmalertmanagerName.Name, Namespace: namespace} + + setupReadyVMAlertmanager := func(ctx context.Context, c *k8stools.ClientWithActions, cr *vmv1beta1.VMAlertmanager) { + // Create objects first + _ = CreateOrUpdateAlertManager(ctx, cr, c) + + // Create pod for StatefulSet to simulate readiness + pod := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: vmalertmanagerName.Name + "-0", + Namespace: vmalertmanagerName.Namespace, + Labels: map[string]string{ + "app.kubernetes.io/name": "vmalertmanager", + "app.kubernetes.io/instance": name, + "app.kubernetes.io/component": "monitoring", + "managed-by": "vm-operator", + "controller-revision-hash": "v1", + }, + OwnerReferences: []metav1.OwnerReference{ + { + APIVersion: "apps/v1", + Kind: "StatefulSet", + Name: vmalertmanagerName.Name, + Controller: ptr.To(true), + }, + }, + }, + Status: corev1.PodStatus{ + Phase: corev1.PodRunning, + Conditions: []corev1.PodCondition{ + {Type: corev1.PodReady, Status: corev1.ConditionTrue}, + }, + }, + } + assert.NoError(t, c.Create(ctx, pod)) + + // Update STS status + sts := &appsv1.StatefulSet{} + if err := c.Get(ctx, vmalertmanagerName, sts); err == nil { + sts.Status.CurrentRevision = "v1" + sts.Status.UpdateRevision = "v1" + sts.Status.ObservedGeneration = sts.Generation + sts.Status.Replicas = 1 + sts.Status.ReadyReplicas = 1 + _ = c.Status().Update(ctx, sts) + } + + // clear actions + c.Actions = nil + } // create vmalertmanager with default config f(args{ @@ -107,146 +140,56 @@ func Test_CreateOrUpdate_Actions(t *testing.T) { }, }) - // update vmalertmanager + // update vmalertmanager with no changes f(args{ cr: &vmv1beta1.VMAlertmanager{ ObjectMeta: objectMeta, Spec: vmv1beta1.VMAlertmanagerSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), }, - RollingUpdateStrategy: appsv1.RollingUpdateStatefulSetStrategyType, }, }, - predefinedObjects: []runtime.Object{ - &corev1.ServiceAccount{ObjectMeta: childObjectMeta}, - &rbacv1.Role{ObjectMeta: childObjectMeta}, - &rbacv1.RoleBinding{ObjectMeta: childObjectMeta}, - &corev1.Service{ - ObjectMeta: childObjectMeta, - Spec: corev1.ServiceSpec{ - ClusterIP: "None", - PublishNotReadyAddresses: true, - Selector: map[string]string{ - "app.kubernetes.io/name": "vmalertmanager", - "app.kubernetes.io/instance": name, - "app.kubernetes.io/component": "monitoring", - "managed-by": "vm-operator", - }, - Ports: []corev1.ServicePort{ - { - Name: "web", - Protocol: "TCP", - Port: 9093, - TargetPort: intstr.FromInt(9093), - }, - { - Name: "tcp-mesh", - Port: 9094, - TargetPort: intstr.FromInt(9094), - Protocol: corev1.ProtocolTCP, - }, - { - Name: "udp-mesh", - Port: 9094, - TargetPort: intstr.FromInt(9094), - Protocol: corev1.ProtocolUDP, - }, - }, - }, - }, - &vmv1beta1.VMServiceScrape{ObjectMeta: childObjectMeta}, - &appsv1.StatefulSet{ - ObjectMeta: childObjectMeta, - Spec: appsv1.StatefulSetSpec{ - ServiceName: vmalertmanagerName.Name, - Replicas: ptr.To(int32(1)), - PodManagementPolicy: appsv1.ParallelPodManagement, - UpdateStrategy: appsv1.StatefulSetUpdateStrategy{ - Type: appsv1.OnDeleteStatefulSetStrategyType, - }, - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - "app.kubernetes.io/name": "vmalertmanager", - "app.kubernetes.io/instance": name, - "app.kubernetes.io/component": "monitoring", - "managed-by": "vm-operator", - }, - }, - Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{ - "app.kubernetes.io/name": "vmalertmanager", - "app.kubernetes.io/instance": name, - "app.kubernetes.io/component": "monitoring", - "managed-by": "vm-operator", - }, - }, - Spec: corev1.PodSpec{ - Volumes: []corev1.Volume{ - { - Name: "alertmanager-db", - VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{}, - }, - }, - }, - }, - }, - }, - Status: appsv1.StatefulSetStatus{ - Replicas: 1, - ReadyReplicas: 1, - UpdatedReplicas: 1, - ObservedGeneration: 1, - }, + preRun: setupReadyVMAlertmanager, + }, + want{ + actions: []k8stools.ClientAction{ + {Verb: "Get", Kind: "ServiceAccount", Resource: vmalertmanagerName}, + {Verb: "Get", Kind: "Role", Resource: vmalertmanagerName}, + {Verb: "Get", Kind: "RoleBinding", Resource: vmalertmanagerName}, + {Verb: "Get", Kind: "Service", Resource: vmalertmanagerName}, + {Verb: "Get", Kind: "VMServiceScrape", Resource: vmalertmanagerName}, + {Verb: "Get", Kind: "StatefulSet", Resource: vmalertmanagerName}, + {Verb: "Get", Kind: "StatefulSet", Resource: vmalertmanagerName}, }, - &corev1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: vmalertmanagerName.Name + "-0", - Namespace: namespace, - Labels: map[string]string{ - "app.kubernetes.io/name": "vmalertmanager", - "app.kubernetes.io/instance": name, - "app.kubernetes.io/component": "monitoring", - "managed-by": "vm-operator", - "controller-revision-hash": "v1", - }, - OwnerReferences: []metav1.OwnerReference{ - { - APIVersion: "apps/v1", - Kind: "StatefulSet", - Name: vmalertmanagerName.Name, - Controller: ptr.To(true), - }, - }, - }, - Status: corev1.PodStatus{ - Phase: corev1.PodRunning, - Conditions: []corev1.PodCondition{ - {Type: corev1.PodReady, Status: corev1.ConditionTrue}, - }, + }) + + // no update on status change + f(args{ + cr: &vmv1beta1.VMAlertmanager{ + ObjectMeta: objectMeta, + Spec: vmv1beta1.VMAlertmanagerSpec{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ + ReplicaCount: ptr.To(int32(1)), }, }, }, + preRun: func(ctx context.Context, c *k8stools.ClientWithActions, cr *vmv1beta1.VMAlertmanager) { + setupReadyVMAlertmanager(ctx, c, cr) + + // Update status to simulate consistency + cr.ParsedLastAppliedSpec = cr.Spec.DeepCopy() + }, }, want{ actions: []k8stools.ClientAction{ + {Verb: "Get", Kind: "PodDisruptionBudget", Resource: vmalertmanagerName}, {Verb: "Get", Kind: "ServiceAccount", Resource: vmalertmanagerName}, - {Verb: "Update", Kind: "ServiceAccount", Resource: vmalertmanagerName}, {Verb: "Get", Kind: "Role", Resource: vmalertmanagerName}, - {Verb: "Update", Kind: "Role", Resource: vmalertmanagerName}, {Verb: "Get", Kind: "RoleBinding", Resource: vmalertmanagerName}, - {Verb: "Update", Kind: "RoleBinding", Resource: vmalertmanagerName}, {Verb: "Get", Kind: "Service", Resource: vmalertmanagerName}, - {Verb: "Delete", Kind: "Service", Resource: vmalertmanagerName}, - {Verb: "Create", Kind: "Service", Resource: vmalertmanagerName}, {Verb: "Get", Kind: "VMServiceScrape", Resource: vmalertmanagerName}, - {Verb: "Update", Kind: "VMServiceScrape", Resource: vmalertmanagerName}, - {Verb: "Get", Kind: "StatefulSet", Resource: vmalertmanagerName}, - {Verb: "Delete", Kind: "StatefulSet", Resource: vmalertmanagerName}, {Verb: "Get", Kind: "StatefulSet", Resource: vmalertmanagerName}, - {Verb: "Create", Kind: "StatefulSet", Resource: vmalertmanagerName}, {Verb: "Get", Kind: "StatefulSet", Resource: vmalertmanagerName}, }, }) diff --git a/internal/controller/operator/factory/vmanomaly/pod.go b/internal/controller/operator/factory/vmanomaly/pod.go index c146be690..7e2d0e547 100644 --- a/internal/controller/operator/factory/vmanomaly/pod.go +++ b/internal/controller/operator/factory/vmanomaly/pod.go @@ -13,7 +13,6 @@ import ( vmv1 "github.com/VictoriaMetrics/operator/api/operator/v1" vmv1beta1 "github.com/VictoriaMetrics/operator/api/operator/v1beta1" - "github.com/VictoriaMetrics/operator/internal/config" "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/build" "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/k8stools" ) @@ -145,10 +144,7 @@ func newPodSpec(cr *vmv1.VMAnomaly, ac *build.AssetsCache) (*corev1.PodSpec, err var initContainers []corev1.Container - cfg := config.MustGetBaseConfig() - useStrictSecurity := ptr.Deref(cr.Spec.UseStrictSecurity, cfg.EnableStrictSecurity) - - build.AddStrictSecuritySettingsToContainers(cr.Spec.SecurityContext, initContainers, useStrictSecurity) + build.AddStrictSecuritySettingsToContainers(initContainers, &cr.Spec.CommonAppsParams) ic, err := k8stools.MergePatchContainers(initContainers, cr.Spec.InitContainers) if err != nil { @@ -182,9 +178,9 @@ func newPodSpec(cr *vmv1.VMAnomaly, ac *build.AssetsCache) (*corev1.PodSpec, err Env: envs, TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, } - container = build.Probe(container, cr) + build.Probe(&container, cr, &cr.Spec.CommonAppsParams) containers := []corev1.Container{container} - build.AddStrictSecuritySettingsToContainers(cr.Spec.SecurityContext, containers, useStrictSecurity) + build.AddStrictSecuritySettingsToContainers(containers, &cr.Spec.CommonAppsParams) containers, err = k8stools.MergePatchContainers(containers, cr.Spec.Containers) if err != nil { return nil, fmt.Errorf("failed to merge containers spec: %w", err) diff --git a/internal/controller/operator/factory/vmanomaly/statefulset.go b/internal/controller/operator/factory/vmanomaly/statefulset.go index 6e584b0c3..bda3c19ec 100644 --- a/internal/controller/operator/factory/vmanomaly/statefulset.go +++ b/internal/controller/operator/factory/vmanomaly/statefulset.go @@ -122,7 +122,6 @@ func newK8sApp(cr *vmv1.VMAnomaly, configHash string, ac *build.AssetsCache) (*a if err != nil { return nil, err } - useStrictSecurity := ptr.Deref(cr.Spec.UseStrictSecurity, false) podAnnotations := cr.PodAnnotations() if len(configHash) > 0 { podAnnotations = labels.Merge(podAnnotations, map[string]string{ @@ -158,7 +157,7 @@ func newK8sApp(cr *vmv1.VMAnomaly, configHash string, ac *build.AssetsCache) (*a if cr.Spec.PersistentVolumeClaimRetentionPolicy != nil { app.Spec.PersistentVolumeClaimRetentionPolicy = cr.Spec.PersistentVolumeClaimRetentionPolicy } - build.StatefulSetAddCommonParams(app, useStrictSecurity, &cr.Spec.CommonApplicationDeploymentParams) + build.StatefulSetAddCommonParams(app, &cr.Spec.CommonAppsParams) app.Spec.Template.Spec.Volumes = append(app.Spec.Template.Spec.Volumes, cr.Spec.Volumes...) cr.Spec.Storage.IntoSTSVolume(cr.GetVolumeName(), &app.Spec) app.Spec.VolumeClaimTemplates = append(app.Spec.VolumeClaimTemplates, cr.Spec.ClaimTemplates...) diff --git a/internal/controller/operator/factory/vmanomaly/statefulset_test.go b/internal/controller/operator/factory/vmanomaly/statefulset_test.go index 83f5e9d06..3c6a573d8 100644 --- a/internal/controller/operator/factory/vmanomaly/statefulset_test.go +++ b/internal/controller/operator/factory/vmanomaly/statefulset_test.go @@ -56,7 +56,7 @@ func TestCreateOrUpdate(t *testing.T) { Labels: map[string]string{"main": "system"}, }, Spec: vmv1.VMAnomalySpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), }, License: &vmv1beta1.License{ @@ -121,8 +121,11 @@ schedulers: Labels: map[string]string{"main": "system"}, }, Spec: vmv1.VMAnomalySpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), + LivenessProbe: &corev1.Probe{ + TimeoutSeconds: 20, + }, }, License: &vmv1beta1.License{ Key: ptr.To("test"), @@ -144,11 +147,6 @@ schedulers: fit_every: 2m fit_window: 3h `, - EmbeddedProbes: &vmv1beta1.EmbeddedProbes{ - LivenessProbe: &corev1.Probe{ - TimeoutSeconds: 20, - }, - }, Reader: &vmv1.VMAnomalyReadersSpec{ DatasourceURL: "http://test.com", SamplingPeriod: "1m", @@ -178,7 +176,7 @@ schedulers: Labels: map[string]string{"main": "system"}, }, Spec: vmv1.VMAnomalySpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), }, License: &vmv1beta1.License{ diff --git a/internal/controller/operator/factory/vmanomaly/vmanomaly_reconcile_test.go b/internal/controller/operator/factory/vmanomaly/vmanomaly_reconcile_test.go index 2f71b3803..e89a7a45d 100644 --- a/internal/controller/operator/factory/vmanomaly/vmanomaly_reconcile_test.go +++ b/internal/controller/operator/factory/vmanomaly/vmanomaly_reconcile_test.go @@ -5,25 +5,20 @@ import ( "testing" "github.com/stretchr/testify/assert" - appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/kubernetes/scheme" "k8s.io/utils/ptr" - "sigs.k8s.io/controller-runtime/pkg/client/fake" vmv1 "github.com/VictoriaMetrics/operator/api/operator/v1" - vmv1beta1 "github.com/VictoriaMetrics/operator/api/operator/v1beta1" "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/build" "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/k8stools" ) func Test_CreateOrUpdate_Actions(t *testing.T) { type args struct { - cr *vmv1.VMAnomaly - predefinedObjects []runtime.Object + cr *vmv1.VMAnomaly + preRun func(ctx context.Context, c *k8stools.ClientWithActions, cr *vmv1.VMAnomaly) } type want struct { actions []k8stools.ClientAction @@ -33,26 +28,15 @@ func Test_CreateOrUpdate_Actions(t *testing.T) { f := func(args args, want want) { t.Helper() - // Use local scheme to avoid global scheme pollution - s := runtime.NewScheme() - _ = scheme.AddToScheme(s) - _ = vmv1.AddToScheme(s) - _ = vmv1beta1.AddToScheme(s) - build.AddDefaults(s) - s.Default(args.cr) - - var actions []k8stools.ClientAction - objInterceptors := k8stools.GetInterceptorsWithObjects() - actionInterceptor := k8stools.NewActionRecordingInterceptor(&actions, &objInterceptors) - - fclient := fake.NewClientBuilder(). - WithScheme(s). - WithStatusSubresource(&vmv1.VMAnomaly{}). - WithRuntimeObjects(args.predefinedObjects...). - WithInterceptorFuncs(actionInterceptor). - Build() - + fclient := k8stools.GetTestClientWithActionsAndObjects(nil) ctx := context.TODO() + build.AddDefaults(fclient.Scheme()) + fclient.Scheme().Default(args.cr) + + if args.preRun != nil { + args.preRun(ctx, fclient, args.cr) + } + err := CreateOrUpdate(ctx, args.cr, fclient) if want.err != nil { assert.Error(t, err) @@ -60,19 +44,19 @@ func Test_CreateOrUpdate_Actions(t *testing.T) { assert.NoError(t, err) } - if !assert.Equal(t, len(want.actions), len(actions)) { - for i, action := range actions { + if !assert.Equal(t, len(want.actions), len(fclient.Actions)) { + for i, action := range fclient.Actions { t.Logf("Action %d: %s %s %s", i, action.Verb, action.Kind, action.Resource) } } for i, action := range want.actions { - if i >= len(actions) { + if i >= len(fclient.Actions) { break } - assert.Equal(t, action.Verb, actions[i].Verb, "idx %d verb", i) - assert.Equal(t, action.Kind, actions[i].Kind, "idx %d kind", i) - assert.Equal(t, action.Resource, actions[i].Resource, "idx %d resource", i) + assert.Equal(t, action.Verb, fclient.Actions[i].Verb, "idx %d verb", i) + assert.Equal(t, action.Kind, fclient.Actions[i].Kind, "idx %d kind", i) + assert.Equal(t, action.Resource, fclient.Actions[i].Resource, "idx %d resource", i) } } @@ -81,7 +65,43 @@ func Test_CreateOrUpdate_Actions(t *testing.T) { vmanomalyName := types.NamespacedName{Namespace: namespace, Name: "vmanomaly-" + name} tlsAssetName := types.NamespacedName{Namespace: namespace, Name: "tls-assets-vmanomaly-" + name} objectMeta := metav1.ObjectMeta{Name: name, Namespace: namespace} - childObjectMeta := metav1.ObjectMeta{Name: vmanomalyName.Name, Namespace: namespace} + + setupReadyVMAnomaly := func(ctx context.Context, c *k8stools.ClientWithActions, cr *vmv1.VMAnomaly) { + // Create objects first + assert.NoError(t, CreateOrUpdate(ctx, cr.DeepCopy(), c)) + + pod := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: vmanomalyName.Name + "-0", + Namespace: vmanomalyName.Namespace, + Labels: map[string]string{ + "app.kubernetes.io/name": "vmanomaly", + "app.kubernetes.io/instance": name, + "app.kubernetes.io/component": "monitoring", + "managed-by": "vm-operator", + "controller-revision-hash": "v1", + }, + OwnerReferences: []metav1.OwnerReference{ + { + APIVersion: "apps/v1", + Kind: "StatefulSet", + Name: vmanomalyName.Name, + Controller: ptr.To(true), + }, + }, + }, + Status: corev1.PodStatus{ + Phase: corev1.PodRunning, + Conditions: []corev1.PodCondition{ + {Type: corev1.PodReady, Status: corev1.ConditionTrue}, + }, + }, + } + assert.NoError(t, c.Create(ctx, pod)) + + // clear actions + c.Actions = nil + } // create vmanomaly with default config f(args{ @@ -137,10 +157,6 @@ schedulers: cr: &vmv1.VMAnomaly{ ObjectMeta: objectMeta, Spec: vmv1.VMAnomalySpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ - ReplicaCount: ptr.To(int32(1)), - }, - RollingUpdateStrategy: appsv1.RollingUpdateStatefulSetStrategyType, ConfigRawYaml: ` models: M1: @@ -166,64 +182,71 @@ schedulers: }, }, }, - predefinedObjects: []runtime.Object{ - &corev1.ServiceAccount{ObjectMeta: childObjectMeta}, - &vmv1beta1.VMPodScrape{ObjectMeta: childObjectMeta}, - &corev1.Secret{ObjectMeta: metav1.ObjectMeta{Namespace: namespace, Name: tlsAssetName.Name}}, - &corev1.Secret{ObjectMeta: childObjectMeta}, - &appsv1.StatefulSet{ - ObjectMeta: childObjectMeta, - Spec: appsv1.StatefulSetSpec{ - Replicas: ptr.To(int32(1)), - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - "app.kubernetes.io/name": "vmanomaly", - "app.kubernetes.io/instance": name, - "app.kubernetes.io/component": "monitoring", - "managed-by": "vm-operator", - }, - }, - Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{ - "app.kubernetes.io/name": "vmanomaly", - "app.kubernetes.io/instance": name, - "app.kubernetes.io/component": "monitoring", - "managed-by": "vm-operator", - }, - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - {Name: "vmanomaly"}, - }, - }, - }, + preRun: func(ctx context.Context, c *k8stools.ClientWithActions, cr *vmv1.VMAnomaly) { + setupReadyVMAnomaly(ctx, c, cr) + // change spec + cr.Spec.Image.Tag = "v1.1.1" + }, + }, + want{ + actions: []k8stools.ClientAction{ + {Verb: "Get", Kind: "ServiceAccount", Resource: vmanomalyName}, + {Verb: "Get", Kind: "VMPodScrape", Resource: vmanomalyName}, + // Secrets + {Verb: "Get", Kind: "Secret", Resource: tlsAssetName}, + {Verb: "Get", Kind: "Secret", Resource: vmanomalyName}, + // StatefulSet + {Verb: "Get", Kind: "StatefulSet", Resource: vmanomalyName}, + {Verb: "Update", Kind: "StatefulSet", Resource: vmanomalyName}, + {Verb: "Get", Kind: "StatefulSet", Resource: vmanomalyName}, + }, + }) + + // no update on status change + f(args{ + cr: &vmv1.VMAnomaly{ + ObjectMeta: objectMeta, + Spec: vmv1.VMAnomalySpec{ + ConfigRawYaml: ` +models: + M1: + class: "zscore" + z_threshold: 2.5 + queries: ["q1"] + schedulers: ["S1"] +reader: + queries: + q1: + expr: "sum(up)" +schedulers: + S1: + class: "periodic" + infer_every: "1m" +`, + Reader: &vmv1.VMAnomalyReadersSpec{ + DatasourceURL: "http://reader-url", + SamplingPeriod: "1m", }, - Status: appsv1.StatefulSetStatus{ - Replicas: 1, - ReadyReplicas: 1, - UpdatedReplicas: 1, - ObservedGeneration: 1, + Writer: &vmv1.VMAnomalyWritersSpec{ + DatasourceURL: "http://writer-url", }, }, }, + preRun: func(ctx context.Context, c *k8stools.ClientWithActions, cr *vmv1.VMAnomaly) { + setupReadyVMAnomaly(ctx, c, cr) + // Update status to simulate consistency + cr.ParsedLastAppliedSpec = cr.Spec.DeepCopy() + }, }, want{ actions: []k8stools.ClientAction{ {Verb: "Get", Kind: "ServiceAccount", Resource: vmanomalyName}, - {Verb: "Update", Kind: "ServiceAccount", Resource: vmanomalyName}, {Verb: "Get", Kind: "VMPodScrape", Resource: vmanomalyName}, - {Verb: "Update", Kind: "VMPodScrape", Resource: vmanomalyName}, // Secrets {Verb: "Get", Kind: "Secret", Resource: tlsAssetName}, - {Verb: "Update", Kind: "Secret", Resource: tlsAssetName}, {Verb: "Get", Kind: "Secret", Resource: vmanomalyName}, - {Verb: "Update", Kind: "Secret", Resource: vmanomalyName}, // StatefulSet {Verb: "Get", Kind: "StatefulSet", Resource: vmanomalyName}, - {Verb: "Delete", Kind: "StatefulSet", Resource: vmanomalyName}, - {Verb: "Get", Kind: "StatefulSet", Resource: vmanomalyName}, - {Verb: "Create", Kind: "StatefulSet", Resource: vmanomalyName}, {Verb: "Get", Kind: "StatefulSet", Resource: vmanomalyName}, }, }) diff --git a/internal/controller/operator/factory/vmauth/vmauth.go b/internal/controller/operator/factory/vmauth/vmauth.go index 044b99fb8..91854033e 100644 --- a/internal/controller/operator/factory/vmauth/vmauth.go +++ b/internal/controller/operator/factory/vmauth/vmauth.go @@ -172,7 +172,7 @@ func newDeployForVMAuth(cr *vmv1beta1.VMAuth) (*appsv1.Deployment, error) { Template: *podSpec, }, } - build.DeploymentAddCommonParams(depSpec, ptr.Deref(cr.Spec.UseStrictSecurity, false), &cr.Spec.CommonApplicationDeploymentParams) + build.DeploymentAddCommonParams(depSpec, &cr.Spec.CommonAppsParams) return depSpec, nil } @@ -222,8 +222,6 @@ func makeSpecForVMAuth(cr *vmv1beta1.VMAuth) (*corev1.PodTemplateSpec, error) { }) } - useStrictSecurity := ptr.Deref(cr.Spec.UseStrictSecurity, false) - var volumes []corev1.Volume var volumeMounts []corev1.VolumeMount var crMounts []corev1.VolumeMount @@ -319,7 +317,7 @@ func makeSpecForVMAuth(cr *vmv1beta1.VMAuth) (*corev1.PodTemplateSpec, error) { configReloader := build.ConfigReloaderContainer(false, cr, crMounts, ss) operatorContainers = append(operatorContainers, configReloader) initContainers = append(initContainers, build.ConfigReloaderContainer(true, cr, crMounts, ss)) - build.AddStrictSecuritySettingsToContainers(cr.Spec.SecurityContext, initContainers, useStrictSecurity) + build.AddStrictSecuritySettingsToContainers(initContainers, &cr.Spec.CommonAppsParams) } ic, err := k8stools.MergePatchContainers(initContainers, cr.Spec.InitContainers) if err != nil { @@ -341,19 +339,19 @@ func makeSpecForVMAuth(cr *vmv1beta1.VMAuth) (*corev1.PodTemplateSpec, error) { TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, ImagePullPolicy: cr.Spec.Image.PullPolicy, } - vmauthContainer = addVMAuthProbes(cr, vmauthContainer) + build.Probe(&vmauthContainer, cr, &cr.Spec.CommonAppsParams) build.AddConfigReloadAuthKeyToApp(&vmauthContainer, cr.Spec.ExtraArgs, &cr.Spec.CommonConfigReloaderParams) // move vmauth container to the 0 index operatorContainers = append([]corev1.Container{vmauthContainer}, operatorContainers...) - build.AddStrictSecuritySettingsToContainers(cr.Spec.SecurityContext, operatorContainers, useStrictSecurity) + build.AddStrictSecuritySettingsToContainers(operatorContainers, &cr.Spec.CommonAppsParams) containers, err := k8stools.MergePatchContainers(operatorContainers, cr.Spec.Containers) if err != nil { return nil, err } - volumes = build.AddServiceAccountTokenVolume(volumes, &cr.Spec.CommonApplicationDeploymentParams) + volumes = build.AddServiceAccountTokenVolume(volumes, &cr.Spec.CommonAppsParams) volumes = build.AddConfigReloadAuthKeyVolume(volumes, &cr.Spec.CommonConfigReloaderParams) return &corev1.PodTemplateSpec{ @@ -677,27 +675,3 @@ func buildScrape(cr *vmv1beta1.VMAuth, svc *corev1.Service) *vmv1beta1.VMService } return b } - -func addVMAuthProbes(cr *vmv1beta1.VMAuth, vmauthContainer corev1.Container) corev1.Container { - if cr.UseProxyProtocol() && cr.Spec.EmbeddedProbes == nil { - probePort := intstr.Parse(cr.ProbePort()) - cr.Spec.EmbeddedProbes = &vmv1beta1.EmbeddedProbes{ - ReadinessProbe: &corev1.Probe{ - ProbeHandler: corev1.ProbeHandler{ - TCPSocket: &corev1.TCPSocketAction{ - Port: probePort, - }, - }, - }, - LivenessProbe: &corev1.Probe{ - ProbeHandler: corev1.ProbeHandler{ - TCPSocket: &corev1.TCPSocketAction{ - Port: probePort, - }, - }, - }, - } - } - vmauthContainer = build.Probe(vmauthContainer, cr) - return vmauthContainer -} diff --git a/internal/controller/operator/factory/vmauth/vmauth_reconcile_test.go b/internal/controller/operator/factory/vmauth/vmauth_reconcile_test.go index 9639f21ce..6d27a8d5d 100644 --- a/internal/controller/operator/factory/vmauth/vmauth_reconcile_test.go +++ b/internal/controller/operator/factory/vmauth/vmauth_reconcile_test.go @@ -5,16 +5,9 @@ import ( "testing" "github.com/stretchr/testify/assert" - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - rbacv1 "k8s.io/api/rbac/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/client-go/kubernetes/scheme" "k8s.io/utils/ptr" - "sigs.k8s.io/controller-runtime/pkg/client/fake" vmv1beta1 "github.com/VictoriaMetrics/operator/api/operator/v1beta1" "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/build" @@ -23,8 +16,8 @@ import ( func Test_CreateOrUpdate_Actions(t *testing.T) { type args struct { - cr *vmv1beta1.VMAuth - predefinedObjects []runtime.Object + cr *vmv1beta1.VMAuth + preRun func(ctx context.Context, c *k8stools.ClientWithActions, cr *vmv1beta1.VMAuth) } type want struct { actions []k8stools.ClientAction @@ -34,25 +27,15 @@ func Test_CreateOrUpdate_Actions(t *testing.T) { f := func(args args, want want) { t.Helper() - // Use local scheme to avoid global scheme pollution - s := runtime.NewScheme() - _ = scheme.AddToScheme(s) - _ = vmv1beta1.AddToScheme(s) - build.AddDefaults(s) - s.Default(args.cr) - - var actions []k8stools.ClientAction - objInterceptors := k8stools.GetInterceptorsWithObjects() - actionInterceptor := k8stools.NewActionRecordingInterceptor(&actions, &objInterceptors) + fclient := k8stools.GetTestClientWithActionsAndObjects(nil) + ctx := context.TODO() + build.AddDefaults(fclient.Scheme()) + fclient.Scheme().Default(args.cr) - fclient := fake.NewClientBuilder(). - WithScheme(s). - WithStatusSubresource(&vmv1beta1.VMAuth{}). - WithRuntimeObjects(args.predefinedObjects...). - WithInterceptorFuncs(actionInterceptor). - Build() + if args.preRun != nil { + args.preRun(ctx, fclient, args.cr) + } - ctx := context.TODO() err := CreateOrUpdate(ctx, args.cr, fclient) if want.err != nil { assert.Error(t, err) @@ -60,19 +43,19 @@ func Test_CreateOrUpdate_Actions(t *testing.T) { assert.NoError(t, err) } - if !assert.Equal(t, len(want.actions), len(actions)) { - for i, action := range actions { + if !assert.Equal(t, len(want.actions), len(fclient.Actions)) { + for i, action := range fclient.Actions { t.Logf("Action %d: %s %s %s", i, action.Verb, action.Kind, action.Resource) } } for i, action := range want.actions { - if i >= len(actions) { + if i >= len(fclient.Actions) { break } - assert.Equal(t, action.Verb, actions[i].Verb, "idx %d verb", i) - assert.Equal(t, action.Kind, actions[i].Kind, "idx %d kind", i) - assert.Equal(t, action.Resource, actions[i].Resource, "idx %d resource", i) + assert.Equal(t, action.Verb, fclient.Actions[i].Verb, "idx %d verb", i) + assert.Equal(t, action.Kind, fclient.Actions[i].Kind, "idx %d kind", i) + assert.Equal(t, action.Resource, fclient.Actions[i].Resource, "idx %d resource", i) } } @@ -81,7 +64,14 @@ func Test_CreateOrUpdate_Actions(t *testing.T) { vmauthName := types.NamespacedName{Namespace: namespace, Name: "vmauth-" + name} configSecretName := types.NamespacedName{Namespace: namespace, Name: "vmauth-config-" + name} objectMeta := metav1.ObjectMeta{Name: name, Namespace: namespace} - childObjectMeta := metav1.ObjectMeta{Name: vmauthName.Name, Namespace: namespace} + + setupReadyVMAuth := func(ctx context.Context, c *k8stools.ClientWithActions, cr *vmv1beta1.VMAuth) { + // Create objects first + assert.NoError(t, CreateOrUpdate(ctx, cr.DeepCopy(), c)) + + // clear actions + c.Actions = nil + } // create vmauth with default config f(args{ @@ -112,92 +102,59 @@ func Test_CreateOrUpdate_Actions(t *testing.T) { }, }) - // update vmauth + // update vmauth with no changes f(args{ cr: &vmv1beta1.VMAuth{ ObjectMeta: objectMeta, Spec: vmv1beta1.VMAuthSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), }, }, }, - predefinedObjects: []runtime.Object{ - &corev1.ServiceAccount{ObjectMeta: childObjectMeta}, - &rbacv1.Role{ObjectMeta: childObjectMeta}, - &rbacv1.RoleBinding{ObjectMeta: childObjectMeta}, - &corev1.Service{ - ObjectMeta: childObjectMeta, - Spec: corev1.ServiceSpec{ - Type: corev1.ServiceTypeClusterIP, - ClusterIP: "10.0.0.1", - Selector: map[string]string{ - "app.kubernetes.io/name": "vmauth", - "app.kubernetes.io/instance": name, - "app.kubernetes.io/component": "monitoring", - "managed-by": "vm-operator", - }, - Ports: []corev1.ServicePort{ - { - Name: "http", - Protocol: "TCP", - Port: 8427, - TargetPort: intstr.Parse("8427"), - }, - }, - }, - }, - &vmv1beta1.VMServiceScrape{ObjectMeta: childObjectMeta}, - &corev1.Secret{ObjectMeta: metav1.ObjectMeta{Name: configSecretName.Name, Namespace: namespace}}, - &appsv1.Deployment{ - ObjectMeta: childObjectMeta, - Spec: appsv1.DeploymentSpec{ - Replicas: ptr.To(int32(1)), - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - "app.kubernetes.io/name": "vmauth", - "app.kubernetes.io/instance": name, - "app.kubernetes.io/component": "monitoring", - "managed-by": "vm-operator", - }, - }, - Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{ - "app.kubernetes.io/name": "vmauth", - "app.kubernetes.io/instance": name, - "app.kubernetes.io/component": "monitoring", - "managed-by": "vm-operator", - }, - }, - }, - }, - Status: appsv1.DeploymentStatus{ - Replicas: 1, - ReadyReplicas: 1, - UpdatedReplicas: 1, - ObservedGeneration: 1, - }, - }, - }, + preRun: setupReadyVMAuth, }, want{ actions: []k8stools.ClientAction{ {Verb: "Get", Kind: "ServiceAccount", Resource: vmauthName}, - {Verb: "Update", Kind: "ServiceAccount", Resource: vmauthName}, {Verb: "Get", Kind: "Role", Resource: vmauthName}, - {Verb: "Update", Kind: "Role", Resource: vmauthName}, {Verb: "Get", Kind: "RoleBinding", Resource: vmauthName}, - {Verb: "Update", Kind: "RoleBinding", Resource: vmauthName}, {Verb: "Get", Kind: "Service", Resource: vmauthName}, - {Verb: "Update", Kind: "Service", Resource: vmauthName}, {Verb: "Get", Kind: "VMServiceScrape", Resource: vmauthName}, - {Verb: "Update", Kind: "VMServiceScrape", Resource: vmauthName}, {Verb: "Get", Kind: "Secret", Resource: configSecretName}, - {Verb: "Update", Kind: "Secret", Resource: configSecretName}, {Verb: "Get", Kind: "Deployment", Resource: vmauthName}, - {Verb: "Update", Kind: "Deployment", Resource: vmauthName}, {Verb: "Get", Kind: "Deployment", Resource: vmauthName}, }, }) + + // no update on status change + f(args{ + cr: &vmv1beta1.VMAuth{ + ObjectMeta: objectMeta, + Spec: vmv1beta1.VMAuthSpec{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ + ReplicaCount: ptr.To(int32(1)), + }, + }, + }, + preRun: func(ctx context.Context, c *k8stools.ClientWithActions, cr *vmv1beta1.VMAuth) { + setupReadyVMAuth(ctx, c, cr) + // Update status to simulate consistency + cr.ParsedLastAppliedSpec = cr.Spec.DeepCopy() + }, + }, want{ + actions: []k8stools.ClientAction{ + {Verb: "Get", Kind: "ServiceAccount", Resource: vmauthName}, + {Verb: "Get", Kind: "Role", Resource: vmauthName}, + {Verb: "Get", Kind: "RoleBinding", Resource: vmauthName}, + {Verb: "Get", Kind: "Service", Resource: vmauthName}, + {Verb: "Get", Kind: "VMServiceScrape", Resource: vmauthName}, + {Verb: "Get", Kind: "Secret", Resource: configSecretName}, + {Verb: "Get", Kind: "Deployment", Resource: vmauthName}, + {Verb: "Get", Kind: "Deployment", Resource: vmauthName}, + {Verb: "Get", Kind: "PodDisruptionBudget", Resource: vmauthName}, + {Verb: "Get", Kind: "Ingress", Resource: vmauthName}, + {Verb: "Get", Kind: "HorizontalPodAutoscaler", Resource: vmauthName}, + }, + }) } diff --git a/internal/controller/operator/factory/vmauth/vmauth_test.go b/internal/controller/operator/factory/vmauth/vmauth_test.go index 40832521a..0939b529e 100644 --- a/internal/controller/operator/factory/vmauth/vmauth_test.go +++ b/internal/controller/operator/factory/vmauth/vmauth_test.go @@ -64,7 +64,7 @@ func TestCreateOrUpdate(t *testing.T) { Namespace: "default", }, Spec: vmv1beta1.VMAuthSpec{ - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ Port: "8427", }, HTTPRoute: &vmv1beta1.EmbeddedHTTPRoute{ @@ -100,12 +100,12 @@ func TestCreateOrUpdate(t *testing.T) { Namespace: "default", }, Spec: vmv1beta1.VMAuthSpec{ - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ Port: "8427", }, }, ParsedLastAppliedSpec: &vmv1beta1.VMAuthSpec{ - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ Port: "8427", }, HTTPRoute: &vmv1beta1.EmbeddedHTTPRoute{ @@ -431,7 +431,7 @@ func TestMakeSpecForAuthOk(t *testing.T) { f(&vmv1beta1.VMAuth{ ObjectMeta: metav1.ObjectMeta{Name: "auth", Namespace: "default"}, Spec: vmv1beta1.VMAuthSpec{ - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ UseDefaultResources: ptr.To(false), Image: vmv1beta1.Image{ Repository: "vm-repo", @@ -545,7 +545,7 @@ serviceaccountname: vmauth-auth f(&vmv1beta1.VMAuth{ ObjectMeta: metav1.ObjectMeta{Name: "auth", Namespace: "default"}, Spec: vmv1beta1.VMAuthSpec{ - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ UseDefaultResources: ptr.To(false), Image: vmv1beta1.Image{ Repository: "vm-repo", @@ -674,7 +674,7 @@ func TestBuildIngressForAuthOk(t *testing.T) { f(&vmv1beta1.VMAuth{ ObjectMeta: metav1.ObjectMeta{Name: "auth", Namespace: "default"}, Spec: vmv1beta1.VMAuthSpec{ - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ UseDefaultResources: ptr.To(false), Image: vmv1beta1.Image{ Repository: "vm-repo", @@ -705,7 +705,7 @@ rules: f(&vmv1beta1.VMAuth{ ObjectMeta: metav1.ObjectMeta{Name: "auth", Namespace: "default"}, Spec: vmv1beta1.VMAuthSpec{ - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ UseDefaultResources: ptr.To(false), Image: vmv1beta1.Image{ Repository: "vm-repo", diff --git a/internal/controller/operator/factory/vmauth/vmusers_config_test.go b/internal/controller/operator/factory/vmauth/vmusers_config_test.go index df67b9f4d..9814794e6 100644 --- a/internal/controller/operator/factory/vmauth/vmusers_config_test.go +++ b/internal/controller/operator/factory/vmauth/vmusers_config_test.go @@ -1958,12 +1958,12 @@ unauthorized_user: }, Spec: vmv1beta1.VMClusterSpec{ VMSelect: &vmv1beta1.VMSelect{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(10)), }, }, VMInsert: &vmv1beta1.VMInsert{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(5)), }, }, diff --git a/internal/controller/operator/factory/vmcluster/vmcluster.go b/internal/controller/operator/factory/vmcluster/vmcluster.go index fa0431e65..6bd119d1e 100644 --- a/internal/controller/operator/factory/vmcluster/vmcluster.go +++ b/internal/controller/operator/factory/vmcluster/vmcluster.go @@ -526,7 +526,7 @@ func genVMSelectSpec(cr *vmv1beta1.VMCluster) (*appsv1.StatefulSet, error) { if cr.Spec.VMSelect.PersistentVolumeClaimRetentionPolicy != nil { stsSpec.Spec.PersistentVolumeClaimRetentionPolicy = cr.Spec.VMSelect.PersistentVolumeClaimRetentionPolicy } - build.StatefulSetAddCommonParams(stsSpec, ptr.Deref(cr.Spec.VMSelect.UseStrictSecurity, false), &cr.Spec.VMSelect.CommonApplicationDeploymentParams) + build.StatefulSetAddCommonParams(stsSpec, &cr.Spec.VMSelect.CommonAppsParams) if cr.Spec.VMSelect.CacheMountPath != "" { cr.Spec.VMSelect.StorageSpec.IntoSTSVolume(cr.Spec.VMSelect.GetCacheMountVolumeName(), &stsSpec.Spec) } @@ -673,10 +673,10 @@ func makePodSpecForVMSelect(cr *vmv1beta1.VMCluster) (*corev1.PodTemplateSpec, e TerminationMessagePath: "/dev/termination-log", } - vmselectContainer = build.Probe(vmselectContainer, cr.Spec.VMSelect) + build.Probe(&vmselectContainer, cr.Spec.VMSelect, &cr.Spec.VMSelect.CommonAppsParams) operatorContainers := []corev1.Container{vmselectContainer} - build.AddStrictSecuritySettingsToContainers(cr.Spec.VMSelect.SecurityContext, operatorContainers, ptr.Deref(cr.Spec.VMSelect.UseStrictSecurity, false)) + build.AddStrictSecuritySettingsToContainers(operatorContainers, &cr.Spec.VMSelect.CommonAppsParams) containers, err := k8stools.MergePatchContainers(operatorContainers, cr.Spec.VMSelect.Containers) if err != nil { return nil, err @@ -753,7 +753,7 @@ func genVMInsertSpec(cr *vmv1beta1.VMCluster) (*appsv1.Deployment, error) { Template: *podSpec, }, } - build.DeploymentAddCommonParams(stsSpec, ptr.Deref(cr.Spec.VMInsert.UseStrictSecurity, false), &cr.Spec.VMInsert.CommonApplicationDeploymentParams) + build.DeploymentAddCommonParams(stsSpec, &cr.Spec.VMInsert.CommonAppsParams) return stsSpec, nil } @@ -877,10 +877,10 @@ func makePodSpecForVMInsert(cr *vmv1beta1.VMCluster) (*corev1.PodTemplateSpec, e TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, } - vminsertContainer = build.Probe(vminsertContainer, cr.Spec.VMInsert) + build.Probe(&vminsertContainer, cr.Spec.VMInsert, &cr.Spec.VMInsert.CommonAppsParams) operatorContainers := []corev1.Container{vminsertContainer} - build.AddStrictSecuritySettingsToContainers(cr.Spec.VMInsert.SecurityContext, operatorContainers, ptr.Deref(cr.Spec.VMInsert.UseStrictSecurity, false)) + build.AddStrictSecuritySettingsToContainers(operatorContainers, &cr.Spec.VMInsert.CommonAppsParams) containers, err := k8stools.MergePatchContainers(operatorContainers, cr.Spec.VMInsert.Containers) if err != nil { return nil, err @@ -952,7 +952,7 @@ func buildVMStorageSpec(ctx context.Context, cr *vmv1beta1.VMCluster) (*appsv1.S if cr.Spec.VMStorage.PersistentVolumeClaimRetentionPolicy != nil { stsSpec.Spec.PersistentVolumeClaimRetentionPolicy = cr.Spec.VMStorage.PersistentVolumeClaimRetentionPolicy } - build.StatefulSetAddCommonParams(stsSpec, ptr.Deref(cr.Spec.VMStorage.UseStrictSecurity, false), &cr.Spec.VMStorage.CommonApplicationDeploymentParams) + build.StatefulSetAddCommonParams(stsSpec, &cr.Spec.VMStorage.CommonAppsParams) storageSpec := cr.Spec.VMStorage.Storage storageSpec.IntoSTSVolume(cr.Spec.VMStorage.GetStorageVolumeName(), &stsSpec.Spec) stsSpec.Spec.VolumeClaimTemplates = append(stsSpec.Spec.VolumeClaimTemplates, cr.Spec.VMStorage.ClaimTemplates...) @@ -1097,8 +1097,7 @@ func makePodSpecForVMStorage(ctx context.Context, cr *vmv1beta1.VMCluster) (*cor TerminationMessagePath: "/dev/termination-log", } - vmstorageContainer = build.Probe(vmstorageContainer, cr.Spec.VMStorage) - + build.Probe(&vmstorageContainer, cr.Spec.VMStorage, &cr.Spec.VMStorage.CommonAppsParams) operatorContainers := []corev1.Container{vmstorageContainer} var initContainers []corev1.Container @@ -1122,14 +1121,13 @@ func makePodSpecForVMStorage(ctx context.Context, cr *vmv1beta1.VMCluster) (*cor } } } - useStrictSecurity := ptr.Deref(cr.Spec.VMStorage.UseStrictSecurity, false) - build.AddStrictSecuritySettingsToContainers(cr.Spec.VMStorage.SecurityContext, initContainers, useStrictSecurity) + build.AddStrictSecuritySettingsToContainers(initContainers, &cr.Spec.VMStorage.CommonAppsParams) ic, err := k8stools.MergePatchContainers(initContainers, cr.Spec.VMStorage.InitContainers) if err != nil { return nil, fmt.Errorf("cannot patch vmstorage init containers: %w", err) } - build.AddStrictSecuritySettingsToContainers(cr.Spec.VMStorage.SecurityContext, operatorContainers, useStrictSecurity) + build.AddStrictSecuritySettingsToContainers(operatorContainers, &cr.Spec.VMStorage.CommonAppsParams) containers, err := k8stools.MergePatchContainers(operatorContainers, cr.Spec.VMStorage.Containers) if err != nil { return nil, fmt.Errorf("cannot patch vmstorage containers: %w", err) @@ -1527,13 +1525,13 @@ func buildVMAuthLBDeployment(cr *vmv1beta1.VMCluster) (*appsv1.Deployment, error ImagePullPolicy: spec.Image.PullPolicy, VolumeMounts: vmounts, } - vmauthLBCnt = build.Probe(vmauthLBCnt, &spec) + build.Probe(&vmauthLBCnt, &spec, &spec.CommonAppsParams) containers := []corev1.Container{ vmauthLBCnt, } var err error - build.AddStrictSecuritySettingsToContainers(spec.SecurityContext, containers, ptr.Deref(spec.UseStrictSecurity, false)) + build.AddStrictSecuritySettingsToContainers(containers, &spec.CommonAppsParams) containers, err = k8stools.MergePatchContainers(containers, spec.Containers) if err != nil { return nil, fmt.Errorf("cannot patch containers: %w", err) @@ -1572,7 +1570,7 @@ func buildVMAuthLBDeployment(cr *vmv1beta1.VMCluster) (*appsv1.Deployment, error }, }, } - build.DeploymentAddCommonParams(lbDep, ptr.Deref(cr.Spec.RequestsLoadBalancer.Spec.UseStrictSecurity, false), &spec.CommonApplicationDeploymentParams) + build.DeploymentAddCommonParams(lbDep, &spec.CommonAppsParams) return lbDep, nil } diff --git a/internal/controller/operator/factory/vmcluster/vmcluster_reconcile_test.go b/internal/controller/operator/factory/vmcluster/vmcluster_reconcile_test.go index ea69df258..59fbc4c47 100644 --- a/internal/controller/operator/factory/vmcluster/vmcluster_reconcile_test.go +++ b/internal/controller/operator/factory/vmcluster/vmcluster_reconcile_test.go @@ -8,12 +8,8 @@ import ( appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/client-go/kubernetes/scheme" "k8s.io/utils/ptr" - "sigs.k8s.io/controller-runtime/pkg/client/fake" vmv1beta1 "github.com/VictoriaMetrics/operator/api/operator/v1beta1" "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/build" @@ -22,8 +18,8 @@ import ( func Test_CreateOrUpdate_Actions(t *testing.T) { type args struct { - cr *vmv1beta1.VMCluster - predefinedObjects []runtime.Object + cr *vmv1beta1.VMCluster + preRun func(ctx context.Context, c *k8stools.ClientWithActions, cr *vmv1beta1.VMCluster) } type want struct { actions []k8stools.ClientAction @@ -33,25 +29,15 @@ func Test_CreateOrUpdate_Actions(t *testing.T) { f := func(args args, want want) { t.Helper() - // Use local scheme to avoid global scheme pollution - s := runtime.NewScheme() - _ = scheme.AddToScheme(s) - _ = vmv1beta1.AddToScheme(s) - build.AddDefaults(s) - s.Default(args.cr) - - var actions []k8stools.ClientAction - objInterceptors := k8stools.GetInterceptorsWithObjects() - actionInterceptor := k8stools.NewActionRecordingInterceptor(&actions, &objInterceptors) + fclient := k8stools.GetTestClientWithActionsAndObjects(nil) + ctx := context.TODO() + build.AddDefaults(fclient.Scheme()) + fclient.Scheme().Default(args.cr) - fclient := fake.NewClientBuilder(). - WithScheme(s). - WithStatusSubresource(&vmv1beta1.VMCluster{}). - WithRuntimeObjects(args.predefinedObjects...). - WithInterceptorFuncs(actionInterceptor). - Build() + if args.preRun != nil { + args.preRun(ctx, fclient, args.cr) + } - ctx := context.TODO() err := CreateOrUpdate(ctx, args.cr, fclient) if want.err != nil { assert.Error(t, err) @@ -59,19 +45,19 @@ func Test_CreateOrUpdate_Actions(t *testing.T) { assert.NoError(t, err) } - if !assert.Equal(t, len(want.actions), len(actions)) { - for i, action := range actions { + if !assert.Equal(t, len(want.actions), len(fclient.Actions)) { + for i, action := range fclient.Actions { t.Logf("Action %d: %s %s %s", i, action.Verb, action.Kind, action.Resource) } } for i, action := range want.actions { - if i >= len(actions) { + if i >= len(fclient.Actions) { break } - assert.Equal(t, action.Verb, actions[i].Verb, "idx %d verb", i) - assert.Equal(t, action.Kind, actions[i].Kind, "idx %d kind", i) - assert.Equal(t, action.Resource, actions[i].Resource, "idx %d resource", i) + assert.Equal(t, action.Verb, fclient.Actions[i].Verb, "idx %d verb", i) + assert.Equal(t, action.Kind, fclient.Actions[i].Kind, "idx %d kind", i) + assert.Equal(t, action.Resource, fclient.Actions[i].Resource, "idx %d resource", i) } } @@ -83,33 +69,86 @@ func Test_CreateOrUpdate_Actions(t *testing.T) { vminsertName := types.NamespacedName{Namespace: namespace, Name: "vminsert-" + name} objectMeta := metav1.ObjectMeta{Name: name, Namespace: namespace} - vmstorageMeta := metav1.ObjectMeta{Name: vmstorageName.Name, Namespace: namespace} - vmselectMeta := metav1.ObjectMeta{Name: vmselectName.Name, Namespace: namespace} - vminsertMeta := metav1.ObjectMeta{Name: vminsertName.Name, Namespace: namespace} - // create vmcluster with all components - f(args{ - cr: &vmv1beta1.VMCluster{ - ObjectMeta: objectMeta, - Spec: vmv1beta1.VMClusterSpec{ - RetentionPeriod: "1", - VMStorage: &vmv1beta1.VMStorage{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ - ReplicaCount: ptr.To(int32(1)), - }, + defaultCR := &vmv1beta1.VMCluster{ + ObjectMeta: objectMeta, + Spec: vmv1beta1.VMClusterSpec{ + RetentionPeriod: "1", + VMStorage: &vmv1beta1.VMStorage{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ + ReplicaCount: ptr.To(int32(1)), }, - VMSelect: &vmv1beta1.VMSelect{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ - ReplicaCount: ptr.To(int32(1)), - }, + }, + VMSelect: &vmv1beta1.VMSelect{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ + ReplicaCount: ptr.To(int32(1)), }, - VMInsert: &vmv1beta1.VMInsert{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ - ReplicaCount: ptr.To(int32(1)), - }, + }, + VMInsert: &vmv1beta1.VMInsert{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ + ReplicaCount: ptr.To(int32(1)), }, }, }, + } + + setupReadyComponents := func(ctx context.Context, c *k8stools.ClientWithActions, cr *vmv1beta1.VMCluster) { + // Create objects first + assert.NoError(t, CreateOrUpdate(ctx, cr.DeepCopy(), c)) + + // Create pods for StatefulSets to simulate readiness + for _, stsName := range []types.NamespacedName{vmstorageName, vmselectName} { + pod := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: stsName.Name + "-0", + Namespace: stsName.Namespace, + Labels: map[string]string{ + "app.kubernetes.io/name": "vmstorage", + "app.kubernetes.io/instance": name, + "app.kubernetes.io/component": "monitoring", + "managed-by": "vm-operator", + "controller-revision-hash": "v1", + }, + OwnerReferences: []metav1.OwnerReference{ + { + APIVersion: "apps/v1", + Kind: "StatefulSet", + Name: stsName.Name, + Controller: ptr.To(true), + }, + }, + }, + Status: corev1.PodStatus{ + Phase: corev1.PodRunning, + Conditions: []corev1.PodCondition{ + {Type: corev1.PodReady, Status: corev1.ConditionTrue}, + }, + }, + } + if stsName == vmselectName { + pod.Labels["app.kubernetes.io/name"] = "vmselect" + } + assert.NoError(t, c.Create(ctx, pod)) + + // Update STS status + sts := &appsv1.StatefulSet{} + if err := c.Get(ctx, stsName, sts); err == nil { + sts.Status.CurrentRevision = "v1" + sts.Status.UpdateRevision = "v1" + sts.Status.ObservedGeneration = sts.Generation + sts.Status.Replicas = 1 + sts.Status.ReadyReplicas = 1 + _ = c.Status().Update(ctx, sts) + } + } + + // clear actions + c.Actions = nil + } + + // create vmcluster with all components + f(args{ + cr: defaultCR.DeepCopy(), }, want{ actions: []k8stools.ClientAction{ @@ -146,243 +185,66 @@ func Test_CreateOrUpdate_Actions(t *testing.T) { }, }) - // update vmcluster with changes + // update vmcluster with no changes f(args{ - cr: &vmv1beta1.VMCluster{ - ObjectMeta: objectMeta, - Spec: vmv1beta1.VMClusterSpec{ - RetentionPeriod: "1", - VMStorage: &vmv1beta1.VMStorage{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ - ReplicaCount: ptr.To(int32(1)), - }, - RollingUpdateStrategy: appsv1.RollingUpdateStatefulSetStrategyType, - }, - VMSelect: &vmv1beta1.VMSelect{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ - ReplicaCount: ptr.To(int32(1)), - }, - RollingUpdateStrategy: appsv1.RollingUpdateStatefulSetStrategyType, - }, - VMInsert: &vmv1beta1.VMInsert{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ - ReplicaCount: ptr.To(int32(1)), - }, - }, - }, - }, - predefinedObjects: []runtime.Object{ - &corev1.ServiceAccount{ObjectMeta: metav1.ObjectMeta{Name: saName.Name, Namespace: namespace}}, - // VMStorage resources - &appsv1.StatefulSet{ - ObjectMeta: vmstorageMeta, - Spec: appsv1.StatefulSetSpec{ - ServiceName: vmstorageName.Name, - Replicas: ptr.To(int32(1)), - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - "app.kubernetes.io/name": "vmstorage", - "app.kubernetes.io/instance": name, - "app.kubernetes.io/component": "monitoring", - "managed-by": "vm-operator", - }, - }, - }, - Status: appsv1.StatefulSetStatus{ - Replicas: 1, - ReadyReplicas: 1, - UpdatedReplicas: 1, - ObservedGeneration: 1, - }, - }, - &corev1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: vmstorageName.Name + "-0", - Namespace: namespace, - Labels: map[string]string{ - "app.kubernetes.io/name": "vmstorage", - "app.kubernetes.io/instance": name, - "app.kubernetes.io/component": "monitoring", - "managed-by": "vm-operator", - "controller-revision-hash": "v1", - }, - OwnerReferences: []metav1.OwnerReference{ - { - APIVersion: "apps/v1", - Kind: "StatefulSet", - Name: vmstorageName.Name, - Controller: ptr.To(true), - }, - }, - }, - Status: corev1.PodStatus{ - Phase: corev1.PodRunning, - Conditions: []corev1.PodCondition{ - {Type: corev1.PodReady, Status: corev1.ConditionTrue}, - }, - }, - }, - &corev1.Service{ - ObjectMeta: vmstorageMeta, - Spec: corev1.ServiceSpec{ - ClusterIP: "None", - Ports: []corev1.ServicePort{ - {Name: "http", Port: 8482, TargetPort: intstr.FromInt(8482), Protocol: "TCP"}, - {Name: "vminsert", Port: 8400, TargetPort: intstr.FromInt(8400), Protocol: "TCP"}, - {Name: "vmselect", Port: 8401, TargetPort: intstr.FromInt(8401), Protocol: "TCP"}, - }, - Selector: map[string]string{ - "app.kubernetes.io/name": "vmstorage", - "app.kubernetes.io/instance": name, - "app.kubernetes.io/component": "monitoring", - "managed-by": "vm-operator", - }, - }, - }, - &vmv1beta1.VMServiceScrape{ObjectMeta: vmstorageMeta}, - // VMSelect resources - &appsv1.StatefulSet{ - ObjectMeta: vmselectMeta, - Spec: appsv1.StatefulSetSpec{ - ServiceName: vmselectName.Name, - Replicas: ptr.To(int32(1)), - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - "app.kubernetes.io/name": "vmselect", - "app.kubernetes.io/instance": name, - "app.kubernetes.io/component": "monitoring", - "managed-by": "vm-operator", - }, - }, - }, - Status: appsv1.StatefulSetStatus{ - Replicas: 1, - ReadyReplicas: 1, - UpdatedReplicas: 1, - ObservedGeneration: 1, - }, - }, - &corev1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: vmselectName.Name + "-0", - Namespace: namespace, - Labels: map[string]string{ - "app.kubernetes.io/name": "vmselect", - "app.kubernetes.io/instance": name, - "app.kubernetes.io/component": "monitoring", - "managed-by": "vm-operator", - "controller-revision-hash": "v1", - }, - OwnerReferences: []metav1.OwnerReference{ - { - APIVersion: "apps/v1", - Kind: "StatefulSet", - Name: vmselectName.Name, - Controller: ptr.To(true), - }, - }, - }, - Status: corev1.PodStatus{ - Phase: corev1.PodRunning, - Conditions: []corev1.PodCondition{ - {Type: corev1.PodReady, Status: corev1.ConditionTrue}, - }, - }, - }, - &corev1.Service{ - ObjectMeta: vmselectMeta, - Spec: corev1.ServiceSpec{ - ClusterIP: "None", - Ports: []corev1.ServicePort{ - {Name: "http", Port: 8481, TargetPort: intstr.FromInt(8481), Protocol: "TCP"}, - }, - Selector: map[string]string{ - "app.kubernetes.io/name": "vmselect", - "app.kubernetes.io/instance": name, - "app.kubernetes.io/component": "monitoring", - "managed-by": "vm-operator", - }, - }, - }, - &vmv1beta1.VMServiceScrape{ObjectMeta: vmselectMeta}, - // VMInsert resources - &appsv1.Deployment{ - ObjectMeta: vminsertMeta, - Spec: appsv1.DeploymentSpec{ - Replicas: ptr.To(int32(1)), - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - "app.kubernetes.io/name": "vminsert", - "app.kubernetes.io/instance": name, - "app.kubernetes.io/component": "monitoring", - "managed-by": "vm-operator", - }, - }, - }, - Status: appsv1.DeploymentStatus{ - Replicas: 1, - ReadyReplicas: 1, - UpdatedReplicas: 1, - ObservedGeneration: 1, - }, - }, - &corev1.Service{ - ObjectMeta: vminsertMeta, - Spec: corev1.ServiceSpec{ - ClusterIP: "None", - Ports: []corev1.ServicePort{ - {Name: "http", Port: 8480, TargetPort: intstr.FromInt(8480), Protocol: "TCP"}, - }, - Selector: map[string]string{ - "app.kubernetes.io/name": "vminsert", - "app.kubernetes.io/instance": name, - "app.kubernetes.io/component": "monitoring", - "managed-by": "vm-operator", - }, - }, + cr: defaultCR.DeepCopy(), + preRun: setupReadyComponents, + }, + want{ + actions: []k8stools.ClientAction{ + {Verb: "Get", Kind: "ServiceAccount", Resource: saName}, + + // VMStorage + {Verb: "Get", Kind: "StatefulSet", Resource: vmstorageName}, + {Verb: "Get", Kind: "StatefulSet", Resource: vmstorageName}, // wait for ready + {Verb: "Get", Kind: "Service", Resource: vmstorageName}, + {Verb: "Get", Kind: "VMServiceScrape", Resource: vmstorageName}, + + // VMSelect + {Verb: "Get", Kind: "StatefulSet", Resource: vmselectName}, + {Verb: "Get", Kind: "StatefulSet", Resource: vmselectName}, // wait for ready + {Verb: "Get", Kind: "Service", Resource: vmselectName}, + {Verb: "Get", Kind: "VMServiceScrape", Resource: vmselectName}, + + // VMInsert + {Verb: "Get", Kind: "Deployment", Resource: vminsertName}, + {Verb: "Get", Kind: "Deployment", Resource: vminsertName}, + {Verb: "Get", Kind: "Service", Resource: vminsertName}, + {Verb: "Get", Kind: "VMServiceScrape", Resource: vminsertName}, }, - &vmv1beta1.VMServiceScrape{ObjectMeta: vminsertMeta}, + }) + + // no update on status change + f(args{ + cr: defaultCR.DeepCopy(), + preRun: func(ctx context.Context, c *k8stools.ClientWithActions, cr *vmv1beta1.VMCluster) { + setupReadyComponents(ctx, c, cr) + // Update status to simulate consistency + cr.Status.UpdateStatus = vmv1beta1.UpdateStatusOperational }, }, want{ actions: []k8stools.ClientAction{ // ServiceAccount {Verb: "Get", Kind: "ServiceAccount", Resource: saName}, - {Verb: "Update", Kind: "ServiceAccount", Resource: saName}, // VMStorage {Verb: "Get", Kind: "StatefulSet", Resource: vmstorageName}, - {Verb: "Delete", Kind: "StatefulSet", Resource: vmstorageName}, - {Verb: "Get", Kind: "StatefulSet", Resource: vmstorageName}, - {Verb: "Create", Kind: "StatefulSet", Resource: vmstorageName}, - {Verb: "Get", Kind: "StatefulSet", Resource: vmstorageName}, + {Verb: "Get", Kind: "StatefulSet", Resource: vmstorageName}, // wait for ready {Verb: "Get", Kind: "Service", Resource: vmstorageName}, - {Verb: "Delete", Kind: "Service", Resource: vmstorageName}, - {Verb: "Create", Kind: "Service", Resource: vmstorageName}, {Verb: "Get", Kind: "VMServiceScrape", Resource: vmstorageName}, - {Verb: "Update", Kind: "VMServiceScrape", Resource: vmstorageName}, // VMSelect {Verb: "Get", Kind: "StatefulSet", Resource: vmselectName}, - {Verb: "Delete", Kind: "StatefulSet", Resource: vmselectName}, - {Verb: "Get", Kind: "StatefulSet", Resource: vmselectName}, - {Verb: "Create", Kind: "StatefulSet", Resource: vmselectName}, - {Verb: "Get", Kind: "StatefulSet", Resource: vmselectName}, + {Verb: "Get", Kind: "StatefulSet", Resource: vmselectName}, // wait for ready {Verb: "Get", Kind: "Service", Resource: vmselectName}, - {Verb: "Delete", Kind: "Service", Resource: vmselectName}, - {Verb: "Create", Kind: "Service", Resource: vmselectName}, {Verb: "Get", Kind: "VMServiceScrape", Resource: vmselectName}, - {Verb: "Update", Kind: "VMServiceScrape", Resource: vmselectName}, // VMInsert {Verb: "Get", Kind: "Deployment", Resource: vminsertName}, - {Verb: "Update", Kind: "Deployment", Resource: vminsertName}, {Verb: "Get", Kind: "Deployment", Resource: vminsertName}, {Verb: "Get", Kind: "Service", Resource: vminsertName}, - {Verb: "Delete", Kind: "Service", Resource: vminsertName}, - {Verb: "Create", Kind: "Service", Resource: vminsertName}, {Verb: "Get", Kind: "VMServiceScrape", Resource: vminsertName}, - {Verb: "Update", Kind: "VMServiceScrape", Resource: vminsertName}, }, }) } diff --git a/internal/controller/operator/factory/vmcluster/vmcluster_test.go b/internal/controller/operator/factory/vmcluster/vmcluster_test.go index 6ef4d2a51..a7f84d6c5 100644 --- a/internal/controller/operator/factory/vmcluster/vmcluster_test.go +++ b/internal/controller/operator/factory/vmcluster/vmcluster_test.go @@ -71,7 +71,7 @@ func TestCreateOrUpdate(t *testing.T) { PodMetadata: &vmv1beta1.EmbeddedObjectMetadata{ Annotations: map[string]string{"key": "value"}, }, - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(0)), }, }, @@ -80,7 +80,7 @@ func TestCreateOrUpdate(t *testing.T) { Annotations: map[string]string{"key": "value"}, Labels: map[string]string{"label": "value2"}, }, - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(2))}, }, @@ -88,7 +88,7 @@ func TestCreateOrUpdate(t *testing.T) { PodMetadata: &vmv1beta1.EmbeddedObjectMetadata{ Annotations: map[string]string{"key": "value"}, }, - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(2))}, }, @@ -117,7 +117,7 @@ func TestCreateOrUpdate(t *testing.T) { RetentionPeriod: "2", ReplicationFactor: ptr.To(int32(2)), VMInsert: &vmv1beta1.VMInsert{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(0))}, InsertPorts: &vmv1beta1.InsertPorts{ GraphitePort: "8025", @@ -143,7 +143,7 @@ func TestCreateOrUpdate(t *testing.T) { RetentionPeriod: "2", ReplicationFactor: ptr.To(int32(2)), VMInsert: &vmv1beta1.VMInsert{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(0))}, InsertPorts: &vmv1beta1.InsertPorts{ GraphitePort: "8025", @@ -180,7 +180,7 @@ func TestCreateOrUpdate(t *testing.T) { RetentionPeriod: "2", ReplicationFactor: ptr.To(int32(2)), VMSelect: &vmv1beta1.VMSelect{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(2))}, HPA: &vmv1beta1.EmbeddedHPA{ MinReplicas: ptr.To(int32(1)), @@ -212,17 +212,17 @@ func TestCreateOrUpdate(t *testing.T) { RetentionPeriod: "2", ReplicationFactor: ptr.To(int32(2)), VMInsert: &vmv1beta1.VMInsert{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(0))}, }, VMStorage: &vmv1beta1.VMStorage{ MaintenanceSelectNodeIDs: []int32{1, 3}, MaintenanceInsertNodeIDs: []int32{0, 1, 2}, - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(10))}, }, VMSelect: &vmv1beta1.VMSelect{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(2))}, }, }, @@ -282,17 +282,17 @@ func TestCreateOrUpdate(t *testing.T) { RetentionPeriod: "2", ReplicationFactor: ptr.To(int32(2)), VMInsert: &vmv1beta1.VMInsert{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(0))}, }, VMStorage: &vmv1beta1.VMStorage{ MaintenanceSelectNodeIDs: []int32{1, 3}, MaintenanceInsertNodeIDs: []int32{0, 1, 2}, - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(10))}, }, VMSelect: &vmv1beta1.VMSelect{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(2))}, }, }, @@ -354,20 +354,20 @@ func TestCreateOrUpdate(t *testing.T) { RequestsLoadBalancer: vmv1beta1.VMAuthLoadBalancer{ Enabled: true, Spec: vmv1beta1.VMAuthLoadBalancerSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(0))}, }, }, VMSelect: &vmv1beta1.VMSelect{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(0))}, }, VMStorage: &vmv1beta1.VMStorage{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(0))}, }, VMInsert: &vmv1beta1.VMInsert{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(0))}, InsertPorts: &vmv1beta1.InsertPorts{ GraphitePort: "8025", @@ -385,7 +385,7 @@ func TestCreateOrUpdate(t *testing.T) { ObjectMeta: metav1.ObjectMeta{Name: "test", Namespace: "default"}, Spec: vmv1beta1.VMClusterSpec{ VMSelect: &vmv1beta1.VMSelect{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(0)), }, VPA: &vmv1beta1.EmbeddedVPA{ @@ -453,7 +453,7 @@ func TestCreateOrUpdate(t *testing.T) { ObjectMeta: metav1.ObjectMeta{Name: "test", Namespace: "default"}, Spec: vmv1beta1.VMClusterSpec{ VMStorage: &vmv1beta1.VMStorage{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(0)), }, VPA: &vmv1beta1.EmbeddedVPA{ @@ -534,7 +534,7 @@ func TestCreateOrUpdate(t *testing.T) { ObjectMeta: metav1.ObjectMeta{Name: "test", Namespace: "default"}, Spec: vmv1beta1.VMClusterSpec{ VMInsert: &vmv1beta1.VMInsert{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(0)), }, VPA: &vmv1beta1.EmbeddedVPA{ @@ -627,7 +627,7 @@ func TestCreateOrUpdate(t *testing.T) { }, Spec: vmv1beta1.VMClusterSpec{ VMInsert: &vmv1beta1.VMInsert{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(0)), }, }, @@ -807,7 +807,7 @@ spec: Spec: vmv1beta1.VMClusterSpec{ VMStorage: &vmv1beta1.VMStorage{}, VMSelect: &vmv1beta1.VMSelect{ - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ Port: "8352", }, }, @@ -849,7 +849,7 @@ spec: ObjectMeta: metav1.ObjectMeta{Name: "test", Namespace: "default-1"}, Spec: vmv1beta1.VMClusterSpec{ VMStorage: &vmv1beta1.VMStorage{}, - VMSelect: &vmv1beta1.VMSelect{CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{Port: "8352"}, + VMSelect: &vmv1beta1.VMSelect{CommonAppsParams: vmv1beta1.CommonAppsParams{Port: "8352"}, ClusterNativePort: "8477", ServiceSpec: &vmv1beta1.AdditionalServiceSpec{Spec: corev1.ServiceSpec{Type: "LoadBalancer"}}}, }, }, ` diff --git a/internal/controller/operator/factory/vmdistributed/util_test.go b/internal/controller/operator/factory/vmdistributed/util_test.go index 71aa5679f..0a79ee54f 100644 --- a/internal/controller/operator/factory/vmdistributed/util_test.go +++ b/internal/controller/operator/factory/vmdistributed/util_test.go @@ -53,7 +53,7 @@ func TestMergeSpecs(t *testing.T) { f(&vmv1beta1.VMClusterSpec{ RetentionPeriod: "1d", VMStorage: &vmv1beta1.VMStorage{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), }, }, @@ -62,7 +62,7 @@ func TestMergeSpecs(t *testing.T) { }, "zone-a", &vmv1beta1.VMClusterSpec{ RetentionPeriod: "30d", VMStorage: &vmv1beta1.VMStorage{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), }, }, @@ -72,7 +72,7 @@ func TestMergeSpecs(t *testing.T) { f(&vmv1beta1.VMClusterSpec{ RetentionPeriod: "1d", VMStorage: &vmv1beta1.VMStorage{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), NodeSelector: map[string]string{ "topology.kubernetes.io/zone": "%ZONE%", @@ -84,7 +84,7 @@ func TestMergeSpecs(t *testing.T) { }, "zone-a", &vmv1beta1.VMClusterSpec{ RetentionPeriod: "30d", VMStorage: &vmv1beta1.VMStorage{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), NodeSelector: map[string]string{ "topology.kubernetes.io/zone": "zone-a", @@ -103,13 +103,13 @@ func TestMergeSpecs(t *testing.T) { } f(&vmv1alpha1.VMDistributedZoneAgentSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ NodeSelector: map[string]string{ "topology.kubernetes.io/zone": "%ZONE%", }, }, }, &vmv1alpha1.VMDistributedZoneAgentSpec{}, "zone-b", &vmv1alpha1.VMDistributedZoneAgentSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ NodeSelector: map[string]string{ "topology.kubernetes.io/zone": "zone-b", }, diff --git a/internal/controller/operator/factory/vmdistributed/vmdistributed_reconcile_test.go b/internal/controller/operator/factory/vmdistributed/vmdistributed_reconcile_test.go index d3009c13b..90f80df12 100644 --- a/internal/controller/operator/factory/vmdistributed/vmdistributed_reconcile_test.go +++ b/internal/controller/operator/factory/vmdistributed/vmdistributed_reconcile_test.go @@ -7,11 +7,8 @@ import ( "github.com/stretchr/testify/assert" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/kubernetes/scheme" "k8s.io/utils/ptr" - "sigs.k8s.io/controller-runtime/pkg/client/fake" vmv1alpha1 "github.com/VictoriaMetrics/operator/api/operator/v1alpha1" vmv1beta1 "github.com/VictoriaMetrics/operator/api/operator/v1beta1" @@ -21,8 +18,8 @@ import ( func Test_CreateOrUpdate_Actions(t *testing.T) { type args struct { - cr *vmv1alpha1.VMDistributed - predefinedObjects []runtime.Object + cr *vmv1alpha1.VMDistributed + preRun func(ctx context.Context, c *k8stools.ClientWithActions, cr *vmv1alpha1.VMDistributed) } type want struct { actions []k8stools.ClientAction @@ -32,32 +29,16 @@ func Test_CreateOrUpdate_Actions(t *testing.T) { f := func(args args, want want) { t.Helper() - // Use local scheme to avoid global scheme pollution - s := runtime.NewScheme() - _ = scheme.AddToScheme(s) - _ = vmv1alpha1.AddToScheme(s) - _ = vmv1beta1.AddToScheme(s) - build.AddDefaults(s) - s.Default(args.cr) - - var actions []k8stools.ClientAction - objInterceptors := k8stools.GetInterceptorsWithObjects() - actionInterceptor := k8stools.NewActionRecordingInterceptor(&actions, &objInterceptors) - - fclient := fake.NewClientBuilder(). - WithScheme(s). - WithStatusSubresource( - &vmv1alpha1.VMDistributed{}, - &vmv1beta1.VMCluster{}, - &vmv1beta1.VMAgent{}, - &vmv1beta1.VMAuth{}, - ). - WithRuntimeObjects(args.predefinedObjects...). - WithInterceptorFuncs(actionInterceptor). - Build() - + fclient := k8stools.GetTestClientWithActionsAndObjects(nil) ctx := context.TODO() + build.AddDefaults(fclient.Scheme()) + fclient.Scheme().Default(args.cr) + synctest.Test(t, func(t *testing.T) { + if args.preRun != nil { + args.preRun(ctx, fclient, args.cr) + } + err := CreateOrUpdate(ctx, args.cr, fclient) if want.err != nil { assert.Error(t, err) @@ -65,19 +46,19 @@ func Test_CreateOrUpdate_Actions(t *testing.T) { assert.NoError(t, err) } - if !assert.Equal(t, len(want.actions), len(actions)) { - for i, action := range actions { + if !assert.Equal(t, len(want.actions), len(fclient.Actions)) { + for i, action := range fclient.Actions { t.Logf("Action %d: %s %s %s", i, action.Verb, action.Kind, action.Resource) } } for i, action := range want.actions { - if i >= len(actions) { + if i >= len(fclient.Actions) { break } - assert.Equal(t, action.Verb, actions[i].Verb, "idx %d verb", i) - assert.Equal(t, action.Kind, actions[i].Kind, "idx %d kind", i) - assert.Equal(t, action.Resource, actions[i].Resource, "idx %d resource", i) + assert.Equal(t, action.Verb, fclient.Actions[i].Verb, "idx %d verb", i) + assert.Equal(t, action.Kind, fclient.Actions[i].Kind, "idx %d kind", i) + assert.Equal(t, action.Resource, fclient.Actions[i].Resource, "idx %d resource", i) } }) } @@ -103,17 +84,17 @@ func Test_CreateOrUpdate_Actions(t *testing.T) { Spec: vmv1beta1.VMClusterSpec{ RetentionPeriod: "1", VMStorage: &vmv1beta1.VMStorage{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), }, }, VMSelect: &vmv1beta1.VMSelect{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), }, }, VMInsert: &vmv1beta1.VMInsert{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), }, }, @@ -128,7 +109,7 @@ func Test_CreateOrUpdate_Actions(t *testing.T) { }, VMAuth: vmv1alpha1.VMDistributedAuth{ Spec: vmv1beta1.VMAuthSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), }, }, @@ -159,6 +140,7 @@ func Test_CreateOrUpdate_Actions(t *testing.T) { }, }) + // create vmagent f(args{ cr: &vmv1alpha1.VMDistributed{ ObjectMeta: objectMeta, @@ -170,17 +152,17 @@ func Test_CreateOrUpdate_Actions(t *testing.T) { Spec: vmv1beta1.VMClusterSpec{ RetentionPeriod: "1", VMStorage: &vmv1beta1.VMStorage{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), }, }, VMSelect: &vmv1beta1.VMSelect{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), }, }, VMInsert: &vmv1beta1.VMInsert{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), }, }, @@ -216,4 +198,79 @@ func Test_CreateOrUpdate_Actions(t *testing.T) { {Verb: "Get", Kind: "VMAgent", Resource: vmAgentName}, }, }) + + // no change on status update + f(args{ + cr: &vmv1alpha1.VMDistributed{ + ObjectMeta: objectMeta, + Spec: vmv1alpha1.VMDistributedSpec{ + Zones: []vmv1alpha1.VMDistributedZone{ + { + Name: zoneName, + VMCluster: vmv1alpha1.VMDistributedZoneCluster{ + Spec: vmv1beta1.VMClusterSpec{ + RetentionPeriod: "1", + VMStorage: &vmv1beta1.VMStorage{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ + ReplicaCount: ptr.To(int32(1)), + }, + }, + VMSelect: &vmv1beta1.VMSelect{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ + ReplicaCount: ptr.To(int32(1)), + }, + }, + VMInsert: &vmv1beta1.VMInsert{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ + ReplicaCount: ptr.To(int32(1)), + }, + }, + }, + }, + VMAgent: vmv1alpha1.VMDistributedZoneAgent{ + Spec: vmv1alpha1.VMDistributedZoneAgentSpec{ + PodMetadata: &vmv1beta1.EmbeddedObjectMetadata{}, + }, + }, + }, + }, + VMAuth: vmv1alpha1.VMDistributedAuth{ + Spec: vmv1beta1.VMAuthSpec{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ + ReplicaCount: ptr.To(int32(1)), + }, + }, + }, + }, + }, + preRun: func(ctx context.Context, c *k8stools.ClientWithActions, cr *vmv1alpha1.VMDistributed) { + // Create objects first + assert.NoError(t, CreateOrUpdate(ctx, cr.DeepCopy(), c)) + + // clear actions + c.Actions = nil + + // Update status to simulate consistency + cr.Status.UpdateStatus = vmv1beta1.UpdateStatusOperational + }, + }, + want{ + actions: []k8stools.ClientAction{ + // getZones + {Verb: "Get", Kind: "VMCluster", Resource: vmClusterName}, + {Verb: "Get", Kind: "VMAgent", Resource: vmAgentName}, + + // reconcile VMCluster + {Verb: "Get", Kind: "VMCluster", Resource: vmClusterName}, + {Verb: "Get", Kind: "VMCluster", Resource: vmClusterName}, + + // reconcile VMAgent + {Verb: "Get", Kind: "VMAgent", Resource: vmAgentName}, + {Verb: "Get", Kind: "VMAgent", Resource: vmAgentName}, + + // reconcile VMAuth + {Verb: "Get", Kind: "VMAuth", Resource: vmAuthLBName}, + {Verb: "Get", Kind: "VMAuth", Resource: vmAuthLBName}, + }, + }) } diff --git a/internal/controller/operator/factory/vmdistributed/vmdistributed_test.go b/internal/controller/operator/factory/vmdistributed/vmdistributed_test.go index 52ec68a6b..9de06f196 100644 --- a/internal/controller/operator/factory/vmdistributed/vmdistributed_test.go +++ b/internal/controller/operator/factory/vmdistributed/vmdistributed_test.go @@ -28,7 +28,7 @@ func newVMAgent(name, namespace string) *vmv1beta1.VMAgent { CreationTimestamp: metav1.Now(), }, Spec: vmv1beta1.VMAgentSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), }, }, @@ -46,17 +46,17 @@ func newVMCluster(name, namespace, version string) *vmv1beta1.VMCluster { Spec: vmv1beta1.VMClusterSpec{ ClusterVersion: version, VMSelect: &vmv1beta1.VMSelect{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), }, }, VMInsert: &vmv1beta1.VMInsert{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), }, }, VMStorage: &vmv1beta1.VMStorage{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), }, }, @@ -65,9 +65,10 @@ func newVMCluster(name, namespace, version string) *vmv1beta1.VMCluster { } type opts struct { - prepare func(*testData) - validate func(context.Context, client.Client, *testData) - actions func(*testData) []action + prepare func(d *testData) + preRun func(c client.Client, d *testData) + validate func(ctx context.Context, rclient client.Client, d *testData) + actions func(d *testData) []action } type testData struct { @@ -173,6 +174,10 @@ func TestCreateOrUpdate(t *testing.T) { return cl.Update(ctx, obj, opts...) }, }) + if o.preRun != nil { + o.preRun(rclient, d) + actions = nil + } ctx := context.Background() synctest.Test(t, func(t *testing.T) { o.validate(ctx, rclient, d) @@ -279,20 +284,13 @@ func TestCreateOrUpdate(t *testing.T) { prepare: func(d *testData) { d.zones.vmclusters[0].Spec.VMSelect.Port = "8481" d.cr.Spec.VMAuth.Name = "vmauth-lb" - d.predefinedObjects = append(d.predefinedObjects, &vmv1beta1.VMAuth{ - ObjectMeta: metav1.ObjectMeta{ - Name: "vmauth-lb", - Namespace: "default", - }, - Spec: vmv1beta1.VMAuthSpec{ - LogLevel: "ERROR", - }, - Status: vmv1beta1.VMAuthStatus{ - StatusMetadata: vmv1beta1.StatusMetadata{ - UpdateStatus: vmv1beta1.UpdateStatusOperational, - }, - }, - }) + d.cr.Spec.VMAuth.Spec.LogLevel = "ERROR" + }, + preRun: func(c client.Client, d *testData) { + clusters := []*vmv1beta1.VMCluster{d.zones.vmclusters[0]} + lb := buildVMAuthLB(d.cr, d.zones.vmagents, clusters) + c.Scheme().Default(lb) + assert.NoError(t, c.Create(context.TODO(), lb)) }, actions: func(d *testData) []action { return []action{ @@ -329,26 +327,12 @@ func TestCreateOrUpdate(t *testing.T) { d.cr.Spec.VMAuth.Spec = vmv1beta1.VMAuthSpec{ LogLevel: "INFO", } - lb := &vmv1beta1.VMAuth{ - ObjectMeta: metav1.ObjectMeta{ - Name: d.cr.Spec.VMAuth.Name, - Namespace: d.cr.Namespace, - OwnerReferences: []metav1.OwnerReference{d.cr.AsOwner()}, - }, - Spec: d.cr.Spec.VMAuth.Spec, - Status: vmv1beta1.VMAuthStatus{ - StatusMetadata: vmv1beta1.StatusMetadata{ - UpdateStatus: vmv1beta1.UpdateStatusOperational, - }, - }, - } - var targetRefs []vmv1beta1.TargetRef - targetRefs = append(targetRefs, vmAgentTargetRef(d.zones.vmagents)) - targetRefs = append(targetRefs, vmClusterTargetRef([]*vmv1beta1.VMCluster{d.zones.vmclusters[0]})) - lb.Spec.UnauthorizedUserAccessSpec = &vmv1beta1.VMAuthUnauthorizedUserAccessSpec{ - TargetRefs: targetRefs, - } - d.predefinedObjects = append(d.predefinedObjects, lb) + }, + preRun: func(c client.Client, d *testData) { + clusters := []*vmv1beta1.VMCluster{d.zones.vmclusters[0]} + lb := buildVMAuthLB(d.cr, d.zones.vmagents, clusters) + c.Scheme().Default(lb) + assert.NoError(t, c.Create(context.TODO(), lb)) }, actions: func(d *testData) []action { return []action{ @@ -449,23 +433,13 @@ func TestCreateOrUpdate(t *testing.T) { d.cr.Spec.VMAuth.Spec = vmv1beta1.VMAuthSpec{ LogLevel: "INFO", } - d.predefinedObjects = append(d.predefinedObjects, &vmv1beta1.VMAuth{ - ObjectMeta: metav1.ObjectMeta{ - Name: "vmauth-lb", - Namespace: "default", - }, - Spec: vmv1beta1.VMAuthSpec{ - // We intentionally don't set a full spec here, - // so the update will populate the missing fields (like Port) - // AND set the owner ref. - LogLevel: "INFO", - }, - Status: vmv1beta1.VMAuthStatus{ - StatusMetadata: vmv1beta1.StatusMetadata{ - UpdateStatus: vmv1beta1.UpdateStatusOperational, - }, - }, - }) + }, + preRun: func(c client.Client, d *testData) { + clusters := []*vmv1beta1.VMCluster{d.zones.vmclusters[0]} + lb := buildVMAuthLB(d.cr, d.zones.vmagents, clusters) + c.Scheme().Default(lb) + lb.OwnerReferences = nil + assert.NoError(t, c.Create(context.TODO(), lb)) }, actions: func(d *testData) []action { return []action{ diff --git a/internal/controller/operator/factory/vmsingle/vmsingle.go b/internal/controller/operator/factory/vmsingle/vmsingle.go index 9fe657e5b..a5b229c37 100644 --- a/internal/controller/operator/factory/vmsingle/vmsingle.go +++ b/internal/controller/operator/factory/vmsingle/vmsingle.go @@ -139,7 +139,7 @@ func newDeploy(ctx context.Context, cr *vmv1beta1.VMSingle) (*appsv1.Deployment, Template: *podSpec, }, } - build.DeploymentAddCommonParams(depSpec, ptr.Deref(cr.Spec.UseStrictSecurity, false), &cr.Spec.CommonApplicationDeploymentParams) + build.DeploymentAddCommonParams(depSpec, &cr.Spec.CommonAppsParams) return depSpec, nil } @@ -249,7 +249,6 @@ func makeSpec(ctx context.Context, cr *vmv1beta1.VMSingle) (*corev1.PodTemplateS volumes, vmMounts = build.LicenseVolumeTo(volumes, vmMounts, cr.Spec.License, vmv1beta1.SecretsDir) args = build.LicenseArgsTo(args, cr.Spec.License, vmv1beta1.SecretsDir) - args = build.AddExtraArgsOverrideDefaults(args, cr.Spec.ExtraArgs, "-") sort.Strings(args) vmsingleContainer := corev1.Container{ @@ -265,10 +264,10 @@ func makeSpec(ctx context.Context, cr *vmv1beta1.VMSingle) (*corev1.PodTemplateS ImagePullPolicy: cr.Spec.Image.PullPolicy, } - vmsingleContainer = build.Probe(vmsingleContainer, cr) + build.Probe(&vmsingleContainer, cr, &cr.Spec.CommonAppsParams) - operatorContainers := []corev1.Container{vmsingleContainer} - var initContainers []corev1.Container + containers := []corev1.Container{vmsingleContainer} + var ic []corev1.Container if cr.Spec.VMBackup != nil { vmBackupManagerContainer, err := build.VMBackupManager(ctx, cr.Spec.VMBackup, cr.Spec.Port, storagePath, commonMounts, cr.Spec.ExtraArgs, false, cr.Spec.License) @@ -276,7 +275,7 @@ func makeSpec(ctx context.Context, cr *vmv1beta1.VMSingle) (*corev1.PodTemplateS return nil, err } if vmBackupManagerContainer != nil { - operatorContainers = append(operatorContainers, *vmBackupManagerContainer) + containers = append(containers, *vmBackupManagerContainer) } if cr.Spec.VMBackup.Restore != nil && cr.Spec.VMBackup.Restore.OnStart != nil && @@ -286,19 +285,19 @@ func makeSpec(ctx context.Context, cr *vmv1beta1.VMSingle) (*corev1.PodTemplateS return nil, err } if vmRestore != nil { - initContainers = append(initContainers, *vmRestore) + ic = append(ic, *vmRestore) } } } - build.AddStrictSecuritySettingsToContainers(cr.Spec.SecurityContext, initContainers, ptr.Deref(cr.Spec.UseStrictSecurity, false)) - ic, err := k8stools.MergePatchContainers(initContainers, cr.Spec.InitContainers) + build.AddStrictSecuritySettingsToContainers(ic, &cr.Spec.CommonAppsParams) + ic, err = k8stools.MergePatchContainers(ic, cr.Spec.InitContainers) if err != nil { return nil, fmt.Errorf("cannot apply initContainer patch: %w", err) } - build.AddStrictSecuritySettingsToContainers(cr.Spec.SecurityContext, operatorContainers, ptr.Deref(cr.Spec.UseStrictSecurity, false)) - containers, err := k8stools.MergePatchContainers(operatorContainers, cr.Spec.Containers) + build.AddStrictSecuritySettingsToContainers(containers, &cr.Spec.CommonAppsParams) + containers, err = k8stools.MergePatchContainers(containers, cr.Spec.Containers) if err != nil { return nil, err } diff --git a/internal/controller/operator/factory/vmsingle/vmsingle_reconcile_test.go b/internal/controller/operator/factory/vmsingle/vmsingle_reconcile_test.go index 28fb10553..090f32e46 100644 --- a/internal/controller/operator/factory/vmsingle/vmsingle_reconcile_test.go +++ b/internal/controller/operator/factory/vmsingle/vmsingle_reconcile_test.go @@ -5,16 +5,9 @@ import ( "testing" "github.com/stretchr/testify/assert" - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - rbacv1 "k8s.io/api/rbac/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/client-go/kubernetes/scheme" "k8s.io/utils/ptr" - "sigs.k8s.io/controller-runtime/pkg/client/fake" vmv1beta1 "github.com/VictoriaMetrics/operator/api/operator/v1beta1" "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/build" @@ -23,8 +16,8 @@ import ( func Test_CreateOrUpdate_Actions(t *testing.T) { type args struct { - cr *vmv1beta1.VMSingle - predefinedObjects []runtime.Object + cr *vmv1beta1.VMSingle + preRun func(ctx context.Context, c *k8stools.ClientWithActions, cr *vmv1beta1.VMSingle) } type want struct { actions []k8stools.ClientAction @@ -34,25 +27,15 @@ func Test_CreateOrUpdate_Actions(t *testing.T) { f := func(args args, want want) { t.Helper() - // Use local scheme to avoid global scheme pollution - s := runtime.NewScheme() - _ = scheme.AddToScheme(s) - _ = vmv1beta1.AddToScheme(s) - build.AddDefaults(s) - s.Default(args.cr) - - var actions []k8stools.ClientAction - objInterceptors := k8stools.GetInterceptorsWithObjects() - actionInterceptor := k8stools.NewActionRecordingInterceptor(&actions, &objInterceptors) + fclient := k8stools.GetTestClientWithActionsAndObjects(nil) + ctx := context.TODO() + build.AddDefaults(fclient.Scheme()) + fclient.Scheme().Default(args.cr) - fclient := fake.NewClientBuilder(). - WithScheme(s). - WithStatusSubresource(&vmv1beta1.VMSingle{}). - WithRuntimeObjects(args.predefinedObjects...). - WithInterceptorFuncs(actionInterceptor). - Build() + if args.preRun != nil { + args.preRun(ctx, fclient, args.cr) + } - ctx := context.TODO() err := CreateOrUpdate(ctx, args.cr, fclient) if want.err != nil { assert.Error(t, err) @@ -60,28 +43,34 @@ func Test_CreateOrUpdate_Actions(t *testing.T) { assert.NoError(t, err) } - if !assert.Equal(t, len(want.actions), len(actions)) { - for i, action := range actions { + if !assert.Equal(t, len(want.actions), len(fclient.Actions)) { + for i, action := range fclient.Actions { t.Logf("Action %d: %s %s %s", i, action.Verb, action.Kind, action.Resource) } } for i, action := range want.actions { - if i >= len(actions) { + if i >= len(fclient.Actions) { break } - assert.Equal(t, action.Verb, actions[i].Verb, "idx %d verb", i) - assert.Equal(t, action.Kind, actions[i].Kind, "idx %d kind", i) - assert.Equal(t, action.Resource, actions[i].Resource, "idx %d resource", i) + assert.Equal(t, action.Verb, fclient.Actions[i].Verb, "idx %d verb", i) + assert.Equal(t, action.Kind, fclient.Actions[i].Kind, "idx %d kind", i) + assert.Equal(t, action.Resource, fclient.Actions[i].Resource, "idx %d resource", i) } } name := "example-single" namespace := "default" vmsingleName := types.NamespacedName{Namespace: namespace, Name: "vmsingle-" + name} - tlsAssetName := types.NamespacedName{Namespace: namespace, Name: "tls-assets-vmsingle-" + name} objectMeta := metav1.ObjectMeta{Name: name, Namespace: namespace} - childObjectMeta := metav1.ObjectMeta{Name: vmsingleName.Name, Namespace: namespace} + + setupReadyVMSingle := func(ctx context.Context, c *k8stools.ClientWithActions, cr *vmv1beta1.VMSingle) { + // Create objects first + assert.NoError(t, CreateOrUpdate(ctx, cr.DeepCopy(), c)) + + // clear actions + c.Actions = nil + } // create vmsingle with default config f(args{ @@ -105,87 +94,52 @@ func Test_CreateOrUpdate_Actions(t *testing.T) { }, }) - // update vmsingle + // update vmsingle with no changes f(args{ cr: &vmv1beta1.VMSingle{ ObjectMeta: objectMeta, Spec: vmv1beta1.VMSingleSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), }, }, }, - predefinedObjects: []runtime.Object{ - &corev1.ServiceAccount{ObjectMeta: childObjectMeta}, - &rbacv1.Role{ObjectMeta: childObjectMeta}, - &rbacv1.RoleBinding{ObjectMeta: childObjectMeta}, - &corev1.Service{ - ObjectMeta: childObjectMeta, - Spec: corev1.ServiceSpec{ - Type: corev1.ServiceTypeClusterIP, - ClusterIP: "10.0.0.1", - Selector: map[string]string{ - "app.kubernetes.io/name": "vmsingle", - "app.kubernetes.io/instance": name, - "app.kubernetes.io/component": "monitoring", - "managed-by": "vm-operator", - }, - Ports: []corev1.ServicePort{ - { - Name: "http", - Protocol: "TCP", - Port: 8428, - TargetPort: intstr.Parse("8428"), - }, - }, - }, + preRun: setupReadyVMSingle, + }, + want{ + actions: []k8stools.ClientAction{ + {Verb: "Get", Kind: "ServiceAccount", Resource: vmsingleName}, + {Verb: "Get", Kind: "Service", Resource: vmsingleName}, + {Verb: "Get", Kind: "VMServiceScrape", Resource: vmsingleName}, + // Deployment + {Verb: "Get", Kind: "Deployment", Resource: vmsingleName}, + {Verb: "Get", Kind: "Deployment", Resource: vmsingleName}, }, - &vmv1beta1.VMServiceScrape{ObjectMeta: childObjectMeta}, - &corev1.Secret{ObjectMeta: childObjectMeta}, - &corev1.Secret{ObjectMeta: metav1.ObjectMeta{Name: tlsAssetName.Name, Namespace: namespace}}, - &appsv1.Deployment{ - ObjectMeta: childObjectMeta, - Spec: appsv1.DeploymentSpec{ - Replicas: ptr.To(int32(1)), - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - "app.kubernetes.io/name": "vmsingle", - "app.kubernetes.io/instance": name, - "app.kubernetes.io/component": "monitoring", - "managed-by": "vm-operator", - }, - }, - Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{ - "app.kubernetes.io/name": "vmsingle", - "app.kubernetes.io/instance": name, - "app.kubernetes.io/component": "monitoring", - "managed-by": "vm-operator", - }, - }, - }, - }, - Status: appsv1.DeploymentStatus{ - Replicas: 1, - ReadyReplicas: 1, - UpdatedReplicas: 1, - ObservedGeneration: 1, + }) + + // no update on status change + f(args{ + cr: &vmv1beta1.VMSingle{ + ObjectMeta: objectMeta, + Spec: vmv1beta1.VMSingleSpec{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ + ReplicaCount: ptr.To(int32(1)), }, }, }, + preRun: func(ctx context.Context, c *k8stools.ClientWithActions, cr *vmv1beta1.VMSingle) { + setupReadyVMSingle(ctx, c, cr) + + // Update status to simulate consistency + cr.ParsedLastAppliedSpec = cr.Spec.DeepCopy() + }, }, want{ actions: []k8stools.ClientAction{ {Verb: "Get", Kind: "ServiceAccount", Resource: vmsingleName}, - {Verb: "Update", Kind: "ServiceAccount", Resource: vmsingleName}, {Verb: "Get", Kind: "Service", Resource: vmsingleName}, - {Verb: "Update", Kind: "Service", Resource: vmsingleName}, {Verb: "Get", Kind: "VMServiceScrape", Resource: vmsingleName}, - {Verb: "Update", Kind: "VMServiceScrape", Resource: vmsingleName}, - // Deployment {Verb: "Get", Kind: "Deployment", Resource: vmsingleName}, - {Verb: "Update", Kind: "Deployment", Resource: vmsingleName}, {Verb: "Get", Kind: "Deployment", Resource: vmsingleName}, }, }) diff --git a/internal/controller/operator/factory/vmsingle/vmsingle_test.go b/internal/controller/operator/factory/vmsingle/vmsingle_test.go index 619b266b0..e56404041 100644 --- a/internal/controller/operator/factory/vmsingle/vmsingle_test.go +++ b/internal/controller/operator/factory/vmsingle/vmsingle_test.go @@ -39,7 +39,7 @@ func TestCreateOrUpdate(t *testing.T) { Namespace: "default", }, Spec: vmv1beta1.VMSingleSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1))}, }, }, @@ -67,7 +67,7 @@ func TestCreateOrUpdate(t *testing.T) { GraphitePort: "8053", OpenTSDBPort: "8054", }, - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1))}, }, }, diff --git a/internal/controller/operator/factory/vtcluster/cluster_test.go b/internal/controller/operator/factory/vtcluster/cluster_test.go index 420e5d6a5..0e1527217 100644 --- a/internal/controller/operator/factory/vtcluster/cluster_test.go +++ b/internal/controller/operator/factory/vtcluster/cluster_test.go @@ -72,17 +72,17 @@ func TestCreateOrUpdate(t *testing.T) { }, Spec: vmv1.VTClusterSpec{ Insert: &vmv1.VTInsert{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(2)), }, }, Storage: &vmv1.VTStorage{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(2)), }, }, Select: &vmv1.VTSelect{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(2)), }, }, @@ -135,7 +135,7 @@ func TestCreateOrUpdate(t *testing.T) { RetentionPeriod: "1w", RetentionMaxDiskSpaceUsageBytes: "5GB", FutureRetention: "2d", - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), }, }, @@ -160,13 +160,13 @@ func TestCreateOrUpdate(t *testing.T) { }, Spec: vmv1.VTClusterSpec{ Select: &vmv1.VTSelect{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), }, }, Storage: &vmv1.VTStorage{ RetentionPeriod: "1w", - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), }, HPA: &vmv1beta1.EmbeddedHPA{ @@ -188,7 +188,7 @@ func TestCreateOrUpdate(t *testing.T) { ObjectMeta: metav1.ObjectMeta{Name: "test", Namespace: "default"}, Spec: vmv1.VTClusterSpec{ Insert: &vmv1.VTInsert{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(0)), }, VPA: &vmv1beta1.EmbeddedVPA{ @@ -245,7 +245,7 @@ func TestCreateOrUpdate(t *testing.T) { ObjectMeta: metav1.ObjectMeta{Name: "test", Namespace: "default"}, Spec: vmv1.VTClusterSpec{ Select: &vmv1.VTSelect{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(0)), }, VPA: &vmv1beta1.EmbeddedVPA{ @@ -313,7 +313,7 @@ func TestCreateOrUpdate(t *testing.T) { ObjectMeta: metav1.ObjectMeta{Name: "test", Namespace: "default"}, Spec: vmv1.VTClusterSpec{ Storage: &vmv1.VTStorage{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(0)), }, VPA: &vmv1beta1.EmbeddedVPA{ @@ -394,7 +394,7 @@ func TestCreateOrUpdate(t *testing.T) { ObjectMeta: metav1.ObjectMeta{Name: "test", Namespace: "default"}, Spec: vmv1.VTClusterSpec{ Insert: &vmv1.VTInsert{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(0)), }, VPA: &vmv1beta1.EmbeddedVPA{ @@ -487,7 +487,7 @@ func TestCreateOrUpdate(t *testing.T) { }, Spec: vmv1.VTClusterSpec{ Insert: &vmv1.VTInsert{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(0)), }, }, diff --git a/internal/controller/operator/factory/vtcluster/insert.go b/internal/controller/operator/factory/vtcluster/insert.go index d99928ba9..76c50d46a 100644 --- a/internal/controller/operator/factory/vtcluster/insert.go +++ b/internal/controller/operator/factory/vtcluster/insert.go @@ -109,7 +109,7 @@ func buildVTInsertDeployment(cr *vmv1.VTCluster) (*appsv1.Deployment, error) { Template: *podSpec, }, } - build.DeploymentAddCommonParams(stsSpec, ptr.Deref(cr.Spec.Insert.UseStrictSecurity, false), &cr.Spec.Insert.CommonApplicationDeploymentParams) + build.DeploymentAddCommonParams(stsSpec, &cr.Spec.Insert.CommonAppsParams) return stsSpec, nil } @@ -212,10 +212,10 @@ func buildVTInsertPodSpec(cr *vmv1.VTCluster) (*corev1.PodTemplateSpec, error) { TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError, } - insertContainers = build.Probe(insertContainers, cr.Spec.Insert) + build.Probe(&insertContainers, cr.Spec.Insert, &cr.Spec.Insert.CommonAppsParams) operatorContainers := []corev1.Container{insertContainers} - build.AddStrictSecuritySettingsToContainers(cr.Spec.Insert.SecurityContext, operatorContainers, ptr.Deref(cr.Spec.Insert.UseStrictSecurity, false)) + build.AddStrictSecuritySettingsToContainers(operatorContainers, &cr.Spec.Insert.CommonAppsParams) containers, err := k8stools.MergePatchContainers(operatorContainers, cr.Spec.Insert.Containers) if err != nil { return nil, err diff --git a/internal/controller/operator/factory/vtcluster/select.go b/internal/controller/operator/factory/vtcluster/select.go index a6fd1b175..3a06b2488 100644 --- a/internal/controller/operator/factory/vtcluster/select.go +++ b/internal/controller/operator/factory/vtcluster/select.go @@ -212,7 +212,7 @@ func buildVTSelectDeployment(cr *vmv1.VTCluster) (*appsv1.Deployment, error) { Template: *podSpec, }, } - build.DeploymentAddCommonParams(depSpec, ptr.Deref(cr.Spec.Select.UseStrictSecurity, false), &cr.Spec.Select.CommonApplicationDeploymentParams) + build.DeploymentAddCommonParams(depSpec, &cr.Spec.Select.CommonAppsParams) return depSpec, nil } @@ -319,10 +319,10 @@ func buildVTSelectPodSpec(cr *vmv1.VTCluster) (*corev1.PodTemplateSpec, error) { TerminationMessagePath: "/dev/termination-log", } - selectContainers = build.Probe(selectContainers, cr.Spec.Select) + build.Probe(&selectContainers, cr.Spec.Select, &cr.Spec.Select.CommonAppsParams) operatorContainers := []corev1.Container{selectContainers} - build.AddStrictSecuritySettingsToContainers(cr.Spec.Select.SecurityContext, operatorContainers, ptr.Deref(cr.Spec.Select.UseStrictSecurity, false)) + build.AddStrictSecuritySettingsToContainers(operatorContainers, &cr.Spec.Select.CommonAppsParams) containers, err := k8stools.MergePatchContainers(operatorContainers, cr.Spec.Select.Containers) if err != nil { return nil, err diff --git a/internal/controller/operator/factory/vtcluster/storage.go b/internal/controller/operator/factory/vtcluster/storage.go index 213e97dbe..7b40f4d98 100644 --- a/internal/controller/operator/factory/vtcluster/storage.go +++ b/internal/controller/operator/factory/vtcluster/storage.go @@ -203,7 +203,7 @@ func buildVTStorageSTSSpec(cr *vmv1.VTCluster) (*appsv1.StatefulSet, error) { if cr.Spec.Storage.PersistentVolumeClaimRetentionPolicy != nil { stsSpec.Spec.PersistentVolumeClaimRetentionPolicy = cr.Spec.Storage.PersistentVolumeClaimRetentionPolicy } - build.StatefulSetAddCommonParams(stsSpec, ptr.Deref(cr.Spec.Storage.UseStrictSecurity, false), &cr.Spec.Storage.CommonApplicationDeploymentParams) + build.StatefulSetAddCommonParams(stsSpec, &cr.Spec.Storage.CommonAppsParams) storageSpec := cr.Spec.Storage.Storage storageSpec.IntoSTSVolume(cr.Spec.Storage.GetStorageVolumeName(), &stsSpec.Spec) stsSpec.Spec.VolumeClaimTemplates = append(stsSpec.Spec.VolumeClaimTemplates, cr.Spec.Storage.ClaimTemplates...) @@ -319,19 +319,18 @@ func buildVTStoragePodSpec(cr *vmv1.VTCluster) (*corev1.PodTemplateSpec, error) TerminationMessagePath: "/dev/termination-log", } - vmstorageContainer = build.Probe(vmstorageContainer, cr.Spec.Storage) + build.Probe(&vmstorageContainer, cr.Spec.Storage, &cr.Spec.Storage.CommonAppsParams) storageContainers := []corev1.Container{vmstorageContainer} var initContainers []corev1.Container - useStrictSecurity := ptr.Deref(cr.Spec.Storage.UseStrictSecurity, false) - build.AddStrictSecuritySettingsToContainers(cr.Spec.Storage.SecurityContext, initContainers, useStrictSecurity) + build.AddStrictSecuritySettingsToContainers(initContainers, &cr.Spec.Storage.CommonAppsParams) ic, err := k8stools.MergePatchContainers(initContainers, cr.Spec.Storage.InitContainers) if err != nil { return nil, fmt.Errorf("cannot patch storage init containers: %w", err) } - build.AddStrictSecuritySettingsToContainers(cr.Spec.Storage.SecurityContext, storageContainers, useStrictSecurity) + build.AddStrictSecuritySettingsToContainers(storageContainers, &cr.Spec.Storage.CommonAppsParams) containers, err := k8stools.MergePatchContainers(storageContainers, cr.Spec.Storage.Containers) if err != nil { return nil, fmt.Errorf("cannot patch storage containers: %w", err) diff --git a/internal/controller/operator/factory/vtcluster/vmauth_lb.go b/internal/controller/operator/factory/vtcluster/vmauth_lb.go index 02bcfb690..90e7d5a4c 100644 --- a/internal/controller/operator/factory/vtcluster/vmauth_lb.go +++ b/internal/controller/operator/factory/vtcluster/vmauth_lb.go @@ -174,13 +174,13 @@ func buildVMauthLBDeployment(cr *vmv1.VTCluster) (*appsv1.Deployment, error) { ImagePullPolicy: spec.Image.PullPolicy, VolumeMounts: vmounts, } - vmauthLBCnt = build.Probe(vmauthLBCnt, &spec) + build.Probe(&vmauthLBCnt, &spec, &spec.CommonAppsParams) containers := []corev1.Container{ vmauthLBCnt, } var err error - build.AddStrictSecuritySettingsToContainers(spec.SecurityContext, containers, ptr.Deref(spec.UseStrictSecurity, false)) + build.AddStrictSecuritySettingsToContainers(containers, &spec.CommonAppsParams) containers, err = k8stools.MergePatchContainers(containers, spec.Containers) if err != nil { return nil, fmt.Errorf("cannot patch containers: %w", err) @@ -220,7 +220,7 @@ func buildVMauthLBDeployment(cr *vmv1.VTCluster) (*appsv1.Deployment, error) { }, }, } - build.DeploymentAddCommonParams(lbDep, ptr.Deref(cr.Spec.RequestsLoadBalancer.Spec.UseStrictSecurity, false), &spec.CommonApplicationDeploymentParams) + build.DeploymentAddCommonParams(lbDep, &spec.CommonAppsParams) return lbDep, nil diff --git a/internal/controller/operator/factory/vtcluster/vtcluster_reconcile_test.go b/internal/controller/operator/factory/vtcluster/vtcluster_reconcile_test.go index 87406f59c..3dc1643c5 100644 --- a/internal/controller/operator/factory/vtcluster/vtcluster_reconcile_test.go +++ b/internal/controller/operator/factory/vtcluster/vtcluster_reconcile_test.go @@ -8,12 +8,8 @@ import ( appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/client-go/kubernetes/scheme" "k8s.io/utils/ptr" - "sigs.k8s.io/controller-runtime/pkg/client/fake" vmv1 "github.com/VictoriaMetrics/operator/api/operator/v1" vmv1beta1 "github.com/VictoriaMetrics/operator/api/operator/v1beta1" @@ -23,8 +19,8 @@ import ( func Test_CreateOrUpdate_Actions(t *testing.T) { type args struct { - cr *vmv1.VTCluster - predefinedObjects []runtime.Object + cr *vmv1.VTCluster + preRun func(ctx context.Context, c *k8stools.ClientWithActions, cr *vmv1.VTCluster) } type want struct { actions []k8stools.ClientAction @@ -34,26 +30,15 @@ func Test_CreateOrUpdate_Actions(t *testing.T) { f := func(args args, want want) { t.Helper() - // Use local scheme to avoid global scheme pollution - s := runtime.NewScheme() - _ = scheme.AddToScheme(s) - _ = vmv1.AddToScheme(s) - _ = vmv1beta1.AddToScheme(s) - build.AddDefaults(s) - s.Default(args.cr) - - var actions []k8stools.ClientAction - objInterceptors := k8stools.GetInterceptorsWithObjects() - actionInterceptor := k8stools.NewActionRecordingInterceptor(&actions, &objInterceptors) + fclient := k8stools.GetTestClientWithActionsAndObjects(nil) + ctx := context.TODO() + build.AddDefaults(fclient.Scheme()) + fclient.Scheme().Default(args.cr) - fclient := fake.NewClientBuilder(). - WithScheme(s). - WithStatusSubresource(&vmv1.VTCluster{}). - WithRuntimeObjects(args.predefinedObjects...). - WithInterceptorFuncs(actionInterceptor). - Build() + if args.preRun != nil { + args.preRun(ctx, fclient, args.cr) + } - ctx := context.TODO() err := CreateOrUpdate(ctx, fclient, args.cr) if want.err != nil { assert.Error(t, err) @@ -61,19 +46,19 @@ func Test_CreateOrUpdate_Actions(t *testing.T) { assert.NoError(t, err) } - if !assert.Equal(t, len(want.actions), len(actions)) { - for i, action := range actions { + if !assert.Equal(t, len(want.actions), len(fclient.Actions)) { + for i, action := range fclient.Actions { t.Logf("Action %d: %s %s %s", i, action.Verb, action.Kind, action.Resource) } } for i, action := range want.actions { - if i >= len(actions) { + if i >= len(fclient.Actions) { break } - assert.Equal(t, action.Verb, actions[i].Verb, "idx %d verb", i) - assert.Equal(t, action.Kind, actions[i].Kind, "idx %d kind", i) - assert.Equal(t, action.Resource, actions[i].Resource, "idx %d resource", i) + assert.Equal(t, action.Verb, fclient.Actions[i].Verb, "idx %d verb", i) + assert.Equal(t, action.Kind, fclient.Actions[i].Kind, "idx %d kind", i) + assert.Equal(t, action.Resource, fclient.Actions[i].Resource, "idx %d resource", i) } } @@ -85,9 +70,6 @@ func Test_CreateOrUpdate_Actions(t *testing.T) { vtinsertName := types.NamespacedName{Namespace: namespace, Name: "vtinsert-" + name} objectMeta := metav1.ObjectMeta{Name: name, Namespace: namespace} - vtstorageMeta := metav1.ObjectMeta{Name: vtstorageName.Name, Namespace: namespace} - vtselectMeta := metav1.ObjectMeta{Name: vtselectName.Name, Namespace: namespace} - vtinsertMeta := metav1.ObjectMeta{Name: vtinsertName.Name, Namespace: namespace} // create vtcluster with all components f(args{ @@ -95,17 +77,17 @@ func Test_CreateOrUpdate_Actions(t *testing.T) { ObjectMeta: objectMeta, Spec: vmv1.VTClusterSpec{ Storage: &vmv1.VTStorage{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), }, }, Select: &vmv1.VTSelect{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), }, }, Insert: &vmv1.VTInsert{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), }, }, @@ -147,191 +129,109 @@ func Test_CreateOrUpdate_Actions(t *testing.T) { }, }) - // update vtcluster with changes + // update vtcluster with no changes f(args{ cr: &vmv1.VTCluster{ ObjectMeta: objectMeta, Spec: vmv1.VTClusterSpec{ Storage: &vmv1.VTStorage{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), }, RollingUpdateStrategy: appsv1.RollingUpdateStatefulSetStrategyType, }, Select: &vmv1.VTSelect{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), }, - UpdateStrategy: ptr.To(appsv1.RollingUpdateDeploymentStrategyType), }, Insert: &vmv1.VTInsert{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), }, - UpdateStrategy: ptr.To(appsv1.RollingUpdateDeploymentStrategyType), }, }, }, - predefinedObjects: []runtime.Object{ - &corev1.ServiceAccount{ObjectMeta: metav1.ObjectMeta{Name: saName.Name, Namespace: namespace}}, - // VTStorage resources - &appsv1.StatefulSet{ - ObjectMeta: vtstorageMeta, - Spec: appsv1.StatefulSetSpec{Replicas: ptr.To(int32(1))}, - Status: appsv1.StatefulSetStatus{ - Replicas: 1, - ReadyReplicas: 1, - UpdatedReplicas: 1, - ObservedGeneration: 1, - }, - }, - &corev1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: vtstorageName.Name + "-0", - Namespace: namespace, - Labels: map[string]string{ - "app.kubernetes.io/name": "vtstorage", - "app.kubernetes.io/instance": name, - "app.kubernetes.io/component": "monitoring", - "managed-by": "vm-operator", - "controller-revision-hash": "v1", + preRun: func(ctx context.Context, c *k8stools.ClientWithActions, cr *vmv1.VTCluster) { + // Create objects first + assert.NoError(t, CreateOrUpdate(ctx, c, cr.DeepCopy())) + + // Set Ready status + sts := &appsv1.StatefulSet{} + if err := c.Get(ctx, vtstorageName, sts); err == nil { + sts.Status.Replicas = 1 + sts.Status.ReadyReplicas = 1 + sts.Status.UpdatedReplicas = 1 + sts.Status.ObservedGeneration = sts.Generation + sts.Status.UpdateRevision = "v1" + _ = c.Status().Update(ctx, sts) + + labels := make(map[string]string) + for k, v := range sts.Spec.Selector.MatchLabels { + labels[k] = v + } + labels["controller-revision-hash"] = "v1" + + // Create pod for StatefulSet to pass rolling update check + pod := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: sts.Name + "-0", + Namespace: sts.Namespace, + Labels: labels, }, - OwnerReferences: []metav1.OwnerReference{ - { - APIVersion: "apps/v1", - Kind: "StatefulSet", - Name: vtstorageName.Name, - Controller: ptr.To(true), + Status: corev1.PodStatus{ + Phase: corev1.PodRunning, + Conditions: []corev1.PodCondition{ + {Type: corev1.PodReady, Status: corev1.ConditionTrue}, }, }, - }, - Status: corev1.PodStatus{ - Phase: corev1.PodRunning, - Conditions: []corev1.PodCondition{ - {Type: corev1.PodReady, Status: corev1.ConditionTrue}, - }, - }, - }, - &corev1.Service{ - ObjectMeta: vtstorageMeta, - Spec: corev1.ServiceSpec{ - ClusterIP: "None", - Ports: []corev1.ServicePort{ - {Name: "http", Port: 8482, TargetPort: intstr.FromInt(8482), Protocol: "TCP"}, - {Name: "vminsert", Port: 8400, TargetPort: intstr.FromInt(8400), Protocol: "TCP"}, - {Name: "vmselect", Port: 8401, TargetPort: intstr.FromInt(8401), Protocol: "TCP"}, - }, - }, - }, - &vmv1beta1.VMServiceScrape{ObjectMeta: vtstorageMeta}, - // VTSelect resources - &appsv1.StatefulSet{ - ObjectMeta: vtselectMeta, - Spec: appsv1.StatefulSetSpec{Replicas: ptr.To(int32(1))}, - Status: appsv1.StatefulSetStatus{ - Replicas: 1, - ReadyReplicas: 1, - UpdatedReplicas: 1, - ObservedGeneration: 1, - }, - }, - &corev1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: vtselectName.Name + "-0", - Namespace: namespace, - Labels: map[string]string{ - "app.kubernetes.io/name": "vtselect", - "app.kubernetes.io/instance": name, - "app.kubernetes.io/component": "monitoring", - "managed-by": "vm-operator", - "controller-revision-hash": "v1", - }, - OwnerReferences: []metav1.OwnerReference{ - { - APIVersion: "apps/v1", - Kind: "StatefulSet", - Name: vtselectName.Name, - Controller: ptr.To(true), - }, - }, - }, - Status: corev1.PodStatus{ - Phase: corev1.PodRunning, - Conditions: []corev1.PodCondition{ - {Type: corev1.PodReady, Status: corev1.ConditionTrue}, - }, - }, - }, - &corev1.Service{ - ObjectMeta: vtselectMeta, - Spec: corev1.ServiceSpec{ - ClusterIP: "None", - Ports: []corev1.ServicePort{ - {Name: "http", Port: 8481, TargetPort: intstr.FromInt(8481), Protocol: "TCP"}, - }, - }, - }, - &vmv1beta1.VMServiceScrape{ObjectMeta: vtselectMeta}, - // VTInsert resources - &appsv1.Deployment{ - ObjectMeta: vtinsertMeta, - Spec: appsv1.DeploymentSpec{Replicas: ptr.To(int32(1))}, - Status: appsv1.DeploymentStatus{ - Replicas: 1, - ReadyReplicas: 1, - UpdatedReplicas: 1, - ObservedGeneration: 1, - }, - }, - &corev1.Service{ - ObjectMeta: vtinsertMeta, - Spec: corev1.ServiceSpec{ - ClusterIP: "None", - Ports: []corev1.ServicePort{ - {Name: "http", Port: 8480, TargetPort: intstr.FromInt(8480), Protocol: "TCP"}, - }, - }, - }, - &vmv1beta1.VMServiceScrape{ObjectMeta: vtinsertMeta}, + } + assert.NoError(t, c.Create(ctx, pod)) + } + + depSelect := &appsv1.Deployment{} + if err := c.Get(ctx, vtselectName, depSelect); err == nil { + depSelect.Status.Replicas = 1 + depSelect.Status.ReadyReplicas = 1 + depSelect.Status.UpdatedReplicas = 1 + depSelect.Status.ObservedGeneration = depSelect.Generation + _ = c.Status().Update(ctx, depSelect) + } + + depInsert := &appsv1.Deployment{} + if err := c.Get(ctx, vtinsertName, depInsert); err == nil { + depInsert.Status.Replicas = 1 + depInsert.Status.ReadyReplicas = 1 + depInsert.Status.UpdatedReplicas = 1 + depInsert.Status.ObservedGeneration = depInsert.Generation + _ = c.Status().Update(ctx, depInsert) + } + + // clear actions + c.Actions = nil + + // set LastAppliedSpec + cr.ParsedLastAppliedSpec = cr.Spec.DeepCopy() }, }, want{ actions: []k8stools.ClientAction{ - // ServiceAccount {Verb: "Get", Kind: "ServiceAccount", Resource: saName}, - {Verb: "Update", Kind: "ServiceAccount", Resource: saName}, - - // VTStorage {Verb: "Get", Kind: "StatefulSet", Resource: vtstorageName}, - {Verb: "Delete", Kind: "StatefulSet", Resource: vtstorageName}, {Verb: "Get", Kind: "StatefulSet", Resource: vtstorageName}, - {Verb: "Create", Kind: "StatefulSet", Resource: vtstorageName}, - {Verb: "Get", Kind: "StatefulSet", Resource: vtstorageName}, // wait for ready {Verb: "Get", Kind: "Service", Resource: vtstorageName}, - {Verb: "Delete", Kind: "Service", Resource: vtstorageName}, - {Verb: "Create", Kind: "Service", Resource: vtstorageName}, {Verb: "Get", Kind: "VMServiceScrape", Resource: vtstorageName}, - {Verb: "Update", Kind: "VMServiceScrape", Resource: vtstorageName}, - - // VTSelect {Verb: "Get", Kind: "Service", Resource: vtselectName}, - {Verb: "Delete", Kind: "Service", Resource: vtselectName}, - {Verb: "Create", Kind: "Service", Resource: vtselectName}, {Verb: "Get", Kind: "VMServiceScrape", Resource: vtselectName}, - {Verb: "Update", Kind: "VMServiceScrape", Resource: vtselectName}, {Verb: "Get", Kind: "Deployment", Resource: vtselectName}, - {Verb: "Create", Kind: "Deployment", Resource: vtselectName}, - {Verb: "Get", Kind: "Deployment", Resource: vtselectName}, // wait for ready - - // VTInsert + {Verb: "Get", Kind: "Deployment", Resource: vtselectName}, + {Verb: "Get", Kind: "Deployment", Resource: vtinsertName}, {Verb: "Get", Kind: "Deployment", Resource: vtinsertName}, - {Verb: "Update", Kind: "Deployment", Resource: vtinsertName}, - {Verb: "Get", Kind: "Deployment", Resource: vtinsertName}, // wait for ready {Verb: "Get", Kind: "Service", Resource: vtinsertName}, - {Verb: "Delete", Kind: "Service", Resource: vtinsertName}, - {Verb: "Create", Kind: "Service", Resource: vtinsertName}, {Verb: "Get", Kind: "VMServiceScrape", Resource: vtinsertName}, - {Verb: "Update", Kind: "VMServiceScrape", Resource: vtinsertName}, + {Verb: "Get", Kind: "Deployment", Resource: types.NamespacedName{Namespace: namespace, Name: "vtclusterlb-" + name}}, + {Verb: "Get", Kind: "Secret", Resource: types.NamespacedName{Namespace: namespace, Name: "vtclusterlb-" + name}}, + {Verb: "Get", Kind: "PodDisruptionBudget", Resource: types.NamespacedName{Namespace: namespace, Name: "vtclusterlb-" + name}}, }, }) } diff --git a/internal/controller/operator/factory/vtsingle/vtsingle.go b/internal/controller/operator/factory/vtsingle/vtsingle.go index 7fe3a4ab9..f5152b8d9 100644 --- a/internal/controller/operator/factory/vtsingle/vtsingle.go +++ b/internal/controller/operator/factory/vtsingle/vtsingle.go @@ -137,7 +137,7 @@ func newDeployment(r *vmv1.VTSingle) (*appsv1.Deployment, error) { Template: *podSpec, }, } - build.DeploymentAddCommonParams(depSpec, ptr.Deref(r.Spec.UseStrictSecurity, false), &r.Spec.CommonApplicationDeploymentParams) + build.DeploymentAddCommonParams(depSpec, &r.Spec.CommonAppsParams) return depSpec, nil } @@ -229,6 +229,7 @@ func makePodSpec(r *vmv1.VTSingle) (*corev1.PodTemplateSpec, error) { MountPath: path.Join(vmv1beta1.ConfigMapsDir, c), }) } + args = build.AddExtraArgsOverrideDefaults(args, r.Spec.ExtraArgs, "-") sort.Strings(args) vtsingleContainer := corev1.Container{ @@ -244,11 +245,11 @@ func makePodSpec(r *vmv1.VTSingle) (*corev1.PodTemplateSpec, error) { ImagePullPolicy: r.Spec.Image.PullPolicy, } - vtsingleContainer = build.Probe(vtsingleContainer, r) + build.Probe(&vtsingleContainer, r, &r.Spec.CommonAppsParams) operatorContainers := []corev1.Container{vtsingleContainer} - build.AddStrictSecuritySettingsToContainers(r.Spec.SecurityContext, operatorContainers, ptr.Deref(r.Spec.UseStrictSecurity, false)) + build.AddStrictSecuritySettingsToContainers(operatorContainers, &r.Spec.CommonAppsParams) containers, err := k8stools.MergePatchContainers(operatorContainers, r.Spec.Containers) if err != nil { diff --git a/internal/controller/operator/factory/vtsingle/vtsingle_reconcile_test.go b/internal/controller/operator/factory/vtsingle/vtsingle_reconcile_test.go index fa94f29ab..23bd9e9e8 100644 --- a/internal/controller/operator/factory/vtsingle/vtsingle_reconcile_test.go +++ b/internal/controller/operator/factory/vtsingle/vtsingle_reconcile_test.go @@ -5,15 +5,9 @@ import ( "testing" "github.com/stretchr/testify/assert" - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/client-go/kubernetes/scheme" "k8s.io/utils/ptr" - "sigs.k8s.io/controller-runtime/pkg/client/fake" vmv1 "github.com/VictoriaMetrics/operator/api/operator/v1" vmv1beta1 "github.com/VictoriaMetrics/operator/api/operator/v1beta1" @@ -23,8 +17,8 @@ import ( func Test_CreateOrUpdate_Actions(t *testing.T) { type args struct { - cr *vmv1.VTSingle - predefinedObjects []runtime.Object + cr *vmv1.VTSingle + preRun func(ctx context.Context, c *k8stools.ClientWithActions, cr *vmv1.VTSingle) } type want struct { actions []k8stools.ClientAction @@ -34,26 +28,15 @@ func Test_CreateOrUpdate_Actions(t *testing.T) { f := func(args args, want want) { t.Helper() - // Use local scheme to avoid global scheme pollution - s := runtime.NewScheme() - _ = scheme.AddToScheme(s) - _ = vmv1.AddToScheme(s) - _ = vmv1beta1.AddToScheme(s) - build.AddDefaults(s) - s.Default(args.cr) - - var actions []k8stools.ClientAction - objInterceptors := k8stools.GetInterceptorsWithObjects() - actionInterceptor := k8stools.NewActionRecordingInterceptor(&actions, &objInterceptors) - - fclient := fake.NewClientBuilder(). - WithScheme(s). - WithStatusSubresource(&vmv1.VTSingle{}). - WithRuntimeObjects(args.predefinedObjects...). - WithInterceptorFuncs(actionInterceptor). - Build() - + fclient := k8stools.GetTestClientWithActionsAndObjects(nil) ctx := context.TODO() + build.AddDefaults(fclient.Scheme()) + fclient.Scheme().Default(args.cr) + + if args.preRun != nil { + args.preRun(ctx, fclient, args.cr) + } + err := CreateOrUpdate(ctx, fclient, args.cr) if want.err != nil { assert.Error(t, err) @@ -61,19 +44,19 @@ func Test_CreateOrUpdate_Actions(t *testing.T) { assert.NoError(t, err) } - if !assert.Equal(t, len(want.actions), len(actions)) { - for i, action := range actions { + if !assert.Equal(t, len(want.actions), len(fclient.Actions)) { + for i, action := range fclient.Actions { t.Logf("Action %d: %s %s %s", i, action.Verb, action.Kind, action.Resource) } } for i, action := range want.actions { - if i >= len(actions) { + if i >= len(fclient.Actions) { break } - assert.Equal(t, action.Verb, actions[i].Verb, "idx %d verb", i) - assert.Equal(t, action.Kind, actions[i].Kind, "idx %d kind", i) - assert.Equal(t, action.Resource, actions[i].Resource, "idx %d resource", i) + assert.Equal(t, action.Verb, fclient.Actions[i].Verb, "idx %d verb", i) + assert.Equal(t, action.Kind, fclient.Actions[i].Kind, "idx %d kind", i) + assert.Equal(t, action.Resource, fclient.Actions[i].Resource, "idx %d resource", i) } } @@ -81,7 +64,14 @@ func Test_CreateOrUpdate_Actions(t *testing.T) { namespace := "default" vtsingleName := types.NamespacedName{Namespace: namespace, Name: "vtsingle-" + name} objectMeta := metav1.ObjectMeta{Name: name, Namespace: namespace} - childObjectMeta := metav1.ObjectMeta{Name: vtsingleName.Name, Namespace: namespace} + + setupReadyVTSingle := func(ctx context.Context, c *k8stools.ClientWithActions, cr *vmv1.VTSingle) { + // Create objects first + assert.NoError(t, CreateOrUpdate(ctx, c, cr.DeepCopy())) + + // clear actions + c.Actions = nil + } // create vtsingle with default config f(args{ @@ -104,82 +94,51 @@ func Test_CreateOrUpdate_Actions(t *testing.T) { }, }) - // update vtsingle + // update vtsingle with no changes f(args{ cr: &vmv1.VTSingle{ ObjectMeta: objectMeta, Spec: vmv1.VTSingleSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), }, }, }, - predefinedObjects: []runtime.Object{ - &corev1.ServiceAccount{ObjectMeta: childObjectMeta}, - &corev1.Service{ - ObjectMeta: childObjectMeta, - Spec: corev1.ServiceSpec{ - Type: corev1.ServiceTypeClusterIP, - ClusterIP: "10.0.0.1", - Selector: map[string]string{ - "app.kubernetes.io/name": "vtsingle", - "app.kubernetes.io/instance": name, - "app.kubernetes.io/component": "monitoring", - "managed-by": "vm-operator", - }, - Ports: []corev1.ServicePort{ - { - Name: "http", - Protocol: "TCP", - Port: 10428, - TargetPort: intstr.Parse("10428"), - }, - }, - }, + preRun: setupReadyVTSingle, + }, + want{ + actions: []k8stools.ClientAction{ + {Verb: "Get", Kind: "ServiceAccount", Resource: vtsingleName}, + {Verb: "Get", Kind: "Service", Resource: vtsingleName}, + {Verb: "Get", Kind: "VMServiceScrape", Resource: vtsingleName}, + {Verb: "Get", Kind: "Deployment", Resource: vtsingleName}, + {Verb: "Get", Kind: "Deployment", Resource: vtsingleName}, }, - &vmv1beta1.VMServiceScrape{ObjectMeta: childObjectMeta}, - &appsv1.Deployment{ - ObjectMeta: childObjectMeta, - Spec: appsv1.DeploymentSpec{ - Replicas: ptr.To(int32(1)), - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - "app.kubernetes.io/name": "vtsingle", - "app.kubernetes.io/instance": name, - "app.kubernetes.io/component": "monitoring", - "managed-by": "vm-operator", - }, - }, - Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{ - "app.kubernetes.io/name": "vtsingle", - "app.kubernetes.io/instance": name, - "app.kubernetes.io/component": "monitoring", - "managed-by": "vm-operator", - }, - }, - }, - }, - Status: appsv1.DeploymentStatus{ - Replicas: 1, - ReadyReplicas: 1, - UpdatedReplicas: 1, - ObservedGeneration: 1, + }) + + // no update on status change + f(args{ + cr: &vmv1.VTSingle{ + ObjectMeta: objectMeta, + Spec: vmv1.VTSingleSpec{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ + ReplicaCount: ptr.To(int32(1)), }, }, }, + preRun: func(ctx context.Context, c *k8stools.ClientWithActions, cr *vmv1.VTSingle) { + setupReadyVTSingle(ctx, c, cr) + + // Update status to simulate consistency + cr.ParsedLastAppliedSpec = cr.Spec.DeepCopy() + }, }, want{ actions: []k8stools.ClientAction{ {Verb: "Get", Kind: "ServiceAccount", Resource: vtsingleName}, - {Verb: "Update", Kind: "ServiceAccount", Resource: vtsingleName}, {Verb: "Get", Kind: "Service", Resource: vtsingleName}, - {Verb: "Update", Kind: "Service", Resource: vtsingleName}, {Verb: "Get", Kind: "VMServiceScrape", Resource: vtsingleName}, - {Verb: "Update", Kind: "VMServiceScrape", Resource: vtsingleName}, {Verb: "Get", Kind: "Deployment", Resource: vtsingleName}, - {Verb: "Update", Kind: "Deployment", Resource: vtsingleName}, {Verb: "Get", Kind: "Deployment", Resource: vtsingleName}, }, }) diff --git a/internal/controller/operator/factory/vtsingle/vtsingle_test.go b/internal/controller/operator/factory/vtsingle/vtsingle_test.go index 6eb3b9dd1..ad44ff663 100644 --- a/internal/controller/operator/factory/vtsingle/vtsingle_test.go +++ b/internal/controller/operator/factory/vtsingle/vtsingle_test.go @@ -46,7 +46,7 @@ func TestCreateOrUpdate(t *testing.T) { Namespace: "default", }, Spec: vmv1.VTSingleSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), }, }, @@ -79,11 +79,9 @@ func TestCreateOrUpdate(t *testing.T) { Namespace: "default", }, Spec: vmv1.VTSingleSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), - }, - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ - Port: "10435", + Port: "10435", }, }, }, @@ -115,11 +113,9 @@ func TestCreateOrUpdate(t *testing.T) { Namespace: "default", }, Spec: vmv1.VTSingleSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), - }, - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ - Port: "10435", + Port: "10435", }, }, }, diff --git a/test/e2e/childobjects/vmrule_test.go b/test/e2e/childobjects/vmrule_test.go index 3a507cb06..72e2d1dce 100644 --- a/test/e2e/childobjects/vmrule_test.go +++ b/test/e2e/childobjects/vmrule_test.go @@ -91,7 +91,7 @@ var _ = Describe("test vmrule Controller", Label("vm", "child", "alert"), func() Datasource: vmv1beta1.VMAlertDatasourceSpec{ URL: "http://localhost:8428", }, - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ExtraArgs: map[string]string{ "notifier.url": "http://test", }, @@ -148,7 +148,7 @@ var _ = Describe("test vmrule Controller", Label("vm", "child", "alert"), func() Datasource: vmv1beta1.VMAlertDatasourceSpec{ URL: "http://localhost:8428", }, - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ExtraArgs: map[string]string{ "notifier.url": "http://test", }, @@ -252,7 +252,7 @@ var _ = Describe("test vmrule Controller", Label("vm", "child", "alert"), func() Datasource: vmv1beta1.VMAlertDatasourceSpec{ URL: "http://localhost:8428", }, - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ExtraArgs: map[string]string{ "notifier.url": "http://test", }, diff --git a/test/e2e/childobjects/vmuser_test.go b/test/e2e/childobjects/vmuser_test.go index d89cbbb50..0a6cd88b6 100644 --- a/test/e2e/childobjects/vmuser_test.go +++ b/test/e2e/childobjects/vmuser_test.go @@ -89,7 +89,7 @@ var _ = Describe("test vmuser Controller", Label("vm", "child", "auth"), func() }, Spec: vmv1beta1.VMAuthSpec{ SelectAllByDefault: true, - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ Image: vmv1beta1.Image{ Tag: "v1.108.0", }, @@ -139,7 +139,7 @@ var _ = Describe("test vmuser Controller", Label("vm", "child", "auth"), func() }, Spec: vmv1beta1.VMAuthSpec{ SelectAllByDefault: true, - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ Image: vmv1beta1.Image{ Tag: "v1.108.0", }, diff --git a/test/e2e/suite/suite.go b/test/e2e/suite/suite.go index be7f95e7a..ea17e41ba 100644 --- a/test/e2e/suite/suite.go +++ b/test/e2e/suite/suite.go @@ -86,6 +86,8 @@ func GetClient(data []byte) client.WithWatch { envs[envName] = rv } } + envName := fmt.Sprintf("VM_%s_TERMINATION_GRACE_PERIOD_SECONDS", prefix) + envs[envName] = "5" } for en, ev := range envs { diff --git a/test/e2e/vlagent_test.go b/test/e2e/vlagent_test.go index 57a2e716b..dc5a048dc 100644 --- a/test/e2e/vlagent_test.go +++ b/test/e2e/vlagent_test.go @@ -65,7 +65,7 @@ var _ = Describe("test vlagent Controller", Label("vl", "agent", "vlagent"), fun Name: nsn.Name, }, Spec: vmv1.VLAgentSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, RemoteWrite: []vmv1.VLAgentRemoteWriteSpec{ @@ -103,7 +103,7 @@ var _ = Describe("test vlagent Controller", Label("vl", "agent", "vlagent"), fun Name: nsn.Name, }, Spec: vmv1.VLAgentSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, RemoteWrite: []vmv1.VLAgentRemoteWriteSpec{ @@ -136,7 +136,7 @@ var _ = Describe("test vlagent Controller", Label("vl", "agent", "vlagent"), fun Name: nsn.Name, }, Spec: vmv1.VLAgentSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, Storage: &vmv1beta1.StorageSpec{ @@ -226,7 +226,7 @@ var _ = Describe("test vlagent Controller", Label("vl", "agent", "vlagent"), fun Name: nsn.Name, }, Spec: vmv1.VLAgentSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, RemoteWrite: []vmv1.VLAgentRemoteWriteSpec{ @@ -338,10 +338,8 @@ var _ = Describe("test vlagent Controller", Label("vl", "agent", "vlagent"), fun Name: nsn.Name, }, Spec: vmv1.VLAgentSpec{ - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ - UseStrictSecurity: ptr.To(true), - }, - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ + UseStrictSecurity: ptr.To(true), ReplicaCount: ptr.To[int32](1), DisableAutomountServiceAccountToken: true, }, @@ -384,7 +382,7 @@ var _ = Describe("test vlagent Controller", Label("vl", "agent", "vlagent"), fun Name: nsn.Name, }, Spec: vmv1.VLAgentSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), ExtraArgs: map[string]string{ "httpListenAddr.useProxyProtocol": "true", @@ -433,7 +431,7 @@ var _ = Describe("test vlagent Controller", Label("vl", "agent", "vlagent"), fun Entry("by scaling replicas to to 3", "update-replicas-3", &vmv1.VLAgent{ Spec: vmv1.VLAgentSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, RemoteWrite: []vmv1.VLAgentRemoteWriteSpec{ @@ -457,9 +455,9 @@ var _ = Describe("test vlagent Controller", Label("vl", "agent", "vlagent"), fun ), Entry("by deleting and restoring PodDisruptionBudget and podScrape", "pdb-mutations-scrape", &vmv1.VLAgent{Spec: vmv1.VLAgentSpec{ - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{UseDefaultResources: ptr.To(false)}, - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ - ReplicaCount: ptr.To[int32](2), + CommonAppsParams: vmv1beta1.CommonAppsParams{ + UseDefaultResources: ptr.To(false), + ReplicaCount: ptr.To[int32](2), }, PodDisruptionBudget: &vmv1beta1.EmbeddedPodDisruptionBudgetSpec{MaxUnavailable: &intstr.IntOrString{IntVal: 1}}, RemoteWrite: []vmv1.VLAgentRemoteWriteSpec{ @@ -499,9 +497,9 @@ var _ = Describe("test vlagent Controller", Label("vl", "agent", "vlagent"), fun ), Entry("by transition into logs collection and back", "logs-collection-transition", &vmv1.VLAgent{Spec: vmv1.VLAgentSpec{ - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{UseDefaultResources: ptr.To(false)}, - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ - ReplicaCount: ptr.To[int32](2), + CommonAppsParams: vmv1beta1.CommonAppsParams{ + UseDefaultResources: ptr.To(false), + ReplicaCount: ptr.To[int32](2), }, PodDisruptionBudget: &vmv1beta1.EmbeddedPodDisruptionBudgetSpec{MaxUnavailable: &intstr.IntOrString{IntVal: 1}}, RemoteWrite: []vmv1.VLAgentRemoteWriteSpec{ diff --git a/test/e2e/vlcluster_test.go b/test/e2e/vlcluster_test.go index 335d2b686..7c46851b9 100644 --- a/test/e2e/vlcluster_test.go +++ b/test/e2e/vlcluster_test.go @@ -48,7 +48,7 @@ var _ = Describe("test vlcluster Controller", Label("vl", "cluster", "vlcluster" VLSelect: &vmv1.VLSelect{}, VLStorage: &vmv1.VLStorage{ RetentionPeriod: "1", - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, @@ -76,14 +76,14 @@ var _ = Describe("test vlcluster Controller", Label("vl", "cluster", "vlcluster" }, Spec: vmv1.VLClusterSpec{ VLInsert: &vmv1.VLInsert{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ExtraArgs: map[string]string{ "httpListenAddr.useProxyProtocol": "true", }, }, }, VLSelect: &vmv1.VLSelect{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ExtraArgs: map[string]string{ "httpListenAddr.useProxyProtocol": "true", }, @@ -91,7 +91,7 @@ var _ = Describe("test vlcluster Controller", Label("vl", "cluster", "vlcluster" }, VLStorage: &vmv1.VLStorage{ RetentionPeriod: "1", - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), ExtraArgs: map[string]string{ "httpListenAddr.useProxyProtocol": "true", @@ -115,7 +115,7 @@ var _ = Describe("test vlcluster Controller", Label("vl", "cluster", "vlcluster" VLSelect: &vmv1.VLSelect{}, VLStorage: &vmv1.VLStorage{ RetentionPeriod: "1", - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, @@ -382,7 +382,7 @@ var _ = Describe("test vlcluster Controller", Label("vl", "cluster", "vlcluster" modify: func(cr *vmv1.VLCluster) { By("upscaling vlselect, removing vlinsert", func() { cr.Spec.VLSelect = &vmv1.VLSelect{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(2)), }, } @@ -408,12 +408,12 @@ var _ = Describe("test vlcluster Controller", Label("vl", "cluster", "vlcluster" modify: func(cr *vmv1.VLCluster) { By("downscaling all components to 0 replicas", func() { cr.Spec.VLSelect = &vmv1.VLSelect{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(0)), }, } cr.Spec.VLInsert = &vmv1.VLInsert{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(0)), }, } diff --git a/test/e2e/vlsingle_test.go b/test/e2e/vlsingle_test.go index 5c4960f9a..04924a227 100644 --- a/test/e2e/vlsingle_test.go +++ b/test/e2e/vlsingle_test.go @@ -61,10 +61,8 @@ var _ = Describe("test vlsingle Controller", Label("vl", "single", "vlsingle"), Namespace: namespace, }, Spec: vmv1.VLSingleSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ - ReplicaCount: ptr.To[int32](1), - }, - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ + ReplicaCount: ptr.To[int32](1), UseStrictSecurity: ptr.To(true), }, RetentionPeriod: "1", @@ -93,10 +91,8 @@ var _ = Describe("test vlsingle Controller", Label("vl", "single", "vlsingle"), Namespace: namespace, }, Spec: vmv1.VLSingleSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ - ReplicaCount: ptr.To[int32](1), - }, - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ + ReplicaCount: ptr.To[int32](1), UseStrictSecurity: ptr.To(false), }, RetentionPeriod: "1", @@ -117,7 +113,7 @@ var _ = Describe("test vlsingle Controller", Label("vl", "single", "vlsingle"), Namespace: namespace, }, Spec: vmv1.VLSingleSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), Volumes: []corev1.Volume{ { @@ -139,8 +135,6 @@ var _ = Describe("test vlsingle Controller", Label("vl", "single", "vlsingle"), MountPath: "/opt/unused/mountpoint", }, }, - }, - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ UseStrictSecurity: ptr.To(false), }, RetentionPeriod: "1", @@ -165,7 +159,7 @@ var _ = Describe("test vlsingle Controller", Label("vl", "single", "vlsingle"), Namespace: namespace, }, Spec: vmv1.VLSingleSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), ExtraArgs: map[string]string{ "httpListenAddr.useProxyProtocol": "true", @@ -184,7 +178,7 @@ var _ = Describe("test vlsingle Controller", Label("vl", "single", "vlsingle"), }, Spec: vmv1.VLSingleSpec{ RetentionPeriod: "10", - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, diff --git a/test/e2e/vmagent_test.go b/test/e2e/vmagent_test.go index 7b2ed92e6..35f934d35 100644 --- a/test/e2e/vmagent_test.go +++ b/test/e2e/vmagent_test.go @@ -53,7 +53,7 @@ var _ = Describe("test vmagent Controller", Label("vm", "agent", "vmagent"), fun RemoteWrite: []vmv1beta1.VMAgentRemoteWriteSpec{ {URL: "http://localhost:8429/api/v1/write"}, }, - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, @@ -154,7 +154,7 @@ var _ = Describe("test vmagent Controller", Label("vm", "agent", "vmagent"), fun Name: nsn.Name, }, Spec: vmv1beta1.VMAgentSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, RemoteWrite: []vmv1beta1.VMAgentRemoteWriteSpec{ @@ -179,10 +179,8 @@ var _ = Describe("test vmagent Controller", Label("vm", "agent", "vmagent"), fun Name: nsn.Name, }, Spec: vmv1beta1.VMAgentSpec{ - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ - UseDefaultResources: ptr.To(false), - }, - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ + UseDefaultResources: ptr.To(false), ReplicaCount: ptr.To[int32](1), DisableAutomountServiceAccountToken: true, }, @@ -209,7 +207,7 @@ var _ = Describe("test vmagent Controller", Label("vm", "agent", "vmagent"), fun Name: nsn.Name, }, Spec: vmv1beta1.VMAgentSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, RemoteWrite: []vmv1beta1.VMAgentRemoteWriteSpec{ @@ -246,7 +244,7 @@ var _ = Describe("test vmagent Controller", Label("vm", "agent", "vmagent"), fun Name: nsn.Name, }, Spec: vmv1beta1.VMAgentSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, RemoteWrite: []vmv1beta1.VMAgentRemoteWriteSpec{ @@ -328,10 +326,8 @@ var _ = Describe("test vmagent Controller", Label("vm", "agent", "vmagent"), fun Name: nsn.Name, }, Spec: vmv1beta1.VMAgentSpec{ - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ - UseStrictSecurity: ptr.To(true), - }, - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ + UseStrictSecurity: ptr.To(true), ReplicaCount: ptr.To[int32](1), DisableAutomountServiceAccountToken: true, }, @@ -393,7 +389,7 @@ var _ = Describe("test vmagent Controller", Label("vm", "agent", "vmagent"), fun Name: nsn.Name, }, Spec: vmv1beta1.VMAgentSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), ExtraArgs: map[string]string{ "httpListenAddr.useProxyProtocol": "true", @@ -412,7 +408,7 @@ var _ = Describe("test vmagent Controller", Label("vm", "agent", "vmagent"), fun Name: nsn.Name, }, Spec: vmv1beta1.VMAgentSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), ExtraArgs: map[string]string{ "httpListenAddr.useProxyProtocol": "true", @@ -432,7 +428,7 @@ var _ = Describe("test vmagent Controller", Label("vm", "agent", "vmagent"), fun Name: nsn.Name, }, Spec: vmv1beta1.VMAgentSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), ExtraArgs: map[string]string{ "httpListenAddr.useProxyProtocol": "true", @@ -482,7 +478,7 @@ var _ = Describe("test vmagent Controller", Label("vm", "agent", "vmagent"), fun Entry("by scaling replicas to 2", "update-replicas-2", &vmv1beta1.VMAgent{ Spec: vmv1beta1.VMAgentSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, RemoteWrite: []vmv1beta1.VMAgentRemoteWriteSpec{ @@ -507,7 +503,7 @@ var _ = Describe("test vmagent Controller", Label("vm", "agent", "vmagent"), fun Entry("by changing revisionHistoryLimit to 3", "update-revision", &vmv1beta1.VMAgent{ Spec: vmv1beta1.VMAgentSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), RevisionHistoryLimitCount: ptr.To[int32](11), }, @@ -536,7 +532,7 @@ var _ = Describe("test vmagent Controller", Label("vm", "agent", "vmagent"), fun Entry("by switching to statefulMode with shard", "stateful-shard", &vmv1beta1.VMAgent{ Spec: vmv1beta1.VMAgentSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, RemoteWrite: []vmv1beta1.VMAgentRemoteWriteSpec{ @@ -569,7 +565,7 @@ var _ = Describe("test vmagent Controller", Label("vm", "agent", "vmagent"), fun Entry("by transition into statefulMode and back", "stateful-transition", &vmv1beta1.VMAgent{ Spec: vmv1beta1.VMAgentSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, RemoteWrite: []vmv1beta1.VMAgentRemoteWriteSpec{ @@ -596,9 +592,9 @@ var _ = Describe("test vmagent Controller", Label("vm", "agent", "vmagent"), fun ), Entry("by deleting and restoring PodDisruptionBudget and serviceScrape", "pdb-mutations-scrape", &vmv1beta1.VMAgent{Spec: vmv1beta1.VMAgentSpec{ - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{UseDefaultResources: ptr.To(false)}, - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ - ReplicaCount: ptr.To[int32](2), + CommonAppsParams: vmv1beta1.CommonAppsParams{ + UseDefaultResources: ptr.To(false), + ReplicaCount: ptr.To[int32](2), }, CommonScrapeParams: vmv1beta1.CommonScrapeParams{ SelectAllByDefault: true, @@ -641,7 +637,7 @@ var _ = Describe("test vmagent Controller", Label("vm", "agent", "vmagent"), fun ), Entry("by transition into daemonSet and back", "daemonset-transition", &vmv1beta1.VMAgent{Spec: vmv1beta1.VMAgentSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, RemoteWrite: []vmv1beta1.VMAgentRemoteWriteSpec{ @@ -690,7 +686,7 @@ var _ = Describe("test vmagent Controller", Label("vm", "agent", "vmagent"), fun RemoteWrite: []vmv1beta1.VMAgentRemoteWriteSpec{ {URL: "http://localhost:8428/api/v1/write"}, }, - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: &initialReplicas, }, }, diff --git a/test/e2e/vmalert_test.go b/test/e2e/vmalert_test.go index 243e2a40f..4cebcd58b 100644 --- a/test/e2e/vmalert_test.go +++ b/test/e2e/vmalert_test.go @@ -48,7 +48,7 @@ var _ = Describe("test vmalert Controller", Label("vm", "alert"), func() { Name: nsn.Name, }, Spec: vmv1beta1.VMAlertSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, Datasource: vmv1beta1.VMAlertDatasourceSpec{ @@ -109,7 +109,7 @@ var _ = Describe("test vmalert Controller", Label("vm", "alert"), func() { Namespace: nsn.Namespace, }, Spec: vmv1beta1.VMAlertSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), ExtraEnvs: []corev1.EnvVar{ { @@ -144,7 +144,7 @@ var _ = Describe("test vmalert Controller", Label("vm", "alert"), func() { Namespace: nsn.Namespace, }, Spec: vmv1beta1.VMAlertSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), Secrets: []string{tlsSecretName}, }, @@ -283,10 +283,8 @@ var _ = Describe("test vmalert Controller", Label("vm", "alert"), func() { Namespace: nsn.Namespace, }, Spec: vmv1beta1.VMAlertSpec{ - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ - UseStrictSecurity: ptr.To(true), - }, - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ + UseStrictSecurity: ptr.To(true), ReplicaCount: ptr.To[int32](1), DisableAutomountServiceAccountToken: true, }, @@ -336,7 +334,7 @@ var _ = Describe("test vmalert Controller", Label("vm", "alert"), func() { Namespace: nsn.Namespace, }, Spec: vmv1beta1.VMAlertSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, Datasource: vmv1beta1.VMAlertDatasourceSpec{ @@ -416,7 +414,7 @@ var _ = Describe("test vmalert Controller", Label("vm", "alert"), func() { Name: nsn.Name, }, Spec: vmv1beta1.VMAlertSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: &initialReplicas, }, Datasource: vmv1beta1.VMAlertDatasourceSpec{ diff --git a/test/e2e/vmalertmanager_test.go b/test/e2e/vmalertmanager_test.go index 10e9f461d..9f5289fe3 100644 --- a/test/e2e/vmalertmanager_test.go +++ b/test/e2e/vmalertmanager_test.go @@ -74,7 +74,7 @@ var _ = Describe("test vmalertmanager Controller", Label("vm", "alertmanager"), Namespace: namespace, }, Spec: vmv1beta1.VMAlertmanagerSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, @@ -94,11 +94,9 @@ var _ = Describe("test vmalertmanager Controller", Label("vm", "alertmanager"), Namespace: namespace, }, Spec: vmv1beta1.VMAlertmanagerSpec{ - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ - UseDefaultResources: ptr.To(false), - UseStrictSecurity: ptr.To(true), - }, - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ + UseDefaultResources: ptr.To(false), + UseStrictSecurity: ptr.To(true), ReplicaCount: ptr.To[int32](1), DisableAutomountServiceAccountToken: true, }, @@ -142,7 +140,7 @@ var _ = Describe("test vmalertmanager Controller", Label("vm", "alertmanager"), Namespace: nsn.Namespace, }, Spec: vmv1beta1.VMAlertmanagerSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, @@ -253,7 +251,7 @@ var _ = Describe("test vmalertmanager Controller", Label("vm", "alertmanager"), Name: nsn.Name, }, Spec: vmv1beta1.VMAlertmanagerSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: &initialReplicas, }, }, diff --git a/test/e2e/vmanomaly_test.go b/test/e2e/vmanomaly_test.go index fe0fff7d7..0a4beddea 100644 --- a/test/e2e/vmanomaly_test.go +++ b/test/e2e/vmanomaly_test.go @@ -152,7 +152,7 @@ var _ = Describe("test vmanomaly Controller", Label("vm", "anomaly", "enterprise Name: nsn.Name, }, Spec: vmv1.VMAnomalySpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, License: &vmv1beta1.License{ @@ -246,10 +246,8 @@ var _ = Describe("test vmanomaly Controller", Label("vm", "anomaly", "enterprise Name: nsn.Name, }, Spec: vmv1.VMAnomalySpec{ - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ - UseStrictSecurity: ptr.To(true), - }, - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ + UseStrictSecurity: ptr.To(true), ReplicaCount: ptr.To[int32](1), DisableAutomountServiceAccountToken: true, }, @@ -337,7 +335,7 @@ var _ = Describe("test vmanomaly Controller", Label("vm", "anomaly", "enterprise Entry("by switching to shard mode", "shard", &vmv1.VMAnomaly{ Spec: vmv1.VMAnomalySpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, License: &vmv1beta1.License{ @@ -382,9 +380,9 @@ var _ = Describe("test vmanomaly Controller", Label("vm", "anomaly", "enterprise Entry("by deleting and restoring PodDisruptionBudget and podScrape", "pdb-mutations-scrape", &vmv1.VMAnomaly{ Spec: vmv1.VMAnomalySpec{ - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{UseDefaultResources: ptr.To(false)}, - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ - ReplicaCount: ptr.To[int32](2), + CommonAppsParams: vmv1beta1.CommonAppsParams{ + UseDefaultResources: ptr.To(false), + ReplicaCount: ptr.To[int32](2), }, License: &vmv1beta1.License{ KeyRef: &corev1.SecretKeySelector{ @@ -450,7 +448,7 @@ var _ = Describe("test vmanomaly Controller", Label("vm", "anomaly", "enterprise Name: nsn.Name, }, Spec: vmv1.VMAnomalySpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(initialReplicas), }, License: &vmv1beta1.License{ diff --git a/test/e2e/vmauth_test.go b/test/e2e/vmauth_test.go index e27d87409..fab76df51 100644 --- a/test/e2e/vmauth_test.go +++ b/test/e2e/vmauth_test.go @@ -58,7 +58,7 @@ var _ = Describe("test vmauth Controller", Label("vm", "auth"), func() { }, Entry("with 1 replica", "replica-1", &vmv1beta1.VMAuth{ Spec: vmv1beta1.VMAuthSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, UnauthorizedUserAccessSpec: &vmv1beta1.VMAuthUnauthorizedUserAccessSpec{ @@ -91,7 +91,7 @@ var _ = Describe("test vmauth Controller", Label("vm", "auth"), func() { }), Entry("with httproute", "httproute", &vmv1beta1.VMAuth{ Spec: vmv1beta1.VMAuthSpec{ - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ Port: "8427", }, HTTPRoute: &vmv1beta1.EmbeddedHTTPRoute{ @@ -127,7 +127,7 @@ var _ = Describe("test vmauth Controller", Label("vm", "auth"), func() { }), Entry("with httproute extrarules", "httproute-extrarules", &vmv1beta1.VMAuth{ Spec: vmv1beta1.VMAuthSpec{ - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ Port: "8427", }, HTTPRoute: &vmv1beta1.EmbeddedHTTPRoute{ @@ -199,11 +199,9 @@ var _ = Describe("test vmauth Controller", Label("vm", "auth"), func() { }), Entry("with strict security", "strict-security", &vmv1beta1.VMAuth{ Spec: vmv1beta1.VMAuthSpec{ - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ - UseStrictSecurity: ptr.To(true), - UseDefaultResources: ptr.To(false), - }, - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ + UseStrictSecurity: ptr.To(true), + UseDefaultResources: ptr.To(false), ReplicaCount: ptr.To[int32](1), DisableAutomountServiceAccountToken: true, }, @@ -287,7 +285,7 @@ var _ = Describe("test vmauth Controller", Label("vm", "auth"), func() { Entry("by changing replicas to 2", "update-replicas-2", &vmv1beta1.VMAuth{ Spec: vmv1beta1.VMAuthSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, UnauthorizedUserAccessSpec: &vmv1beta1.VMAuthUnauthorizedUserAccessSpec{ @@ -322,7 +320,7 @@ var _ = Describe("test vmauth Controller", Label("vm", "auth"), func() { &vmv1beta1.VMAuth{ Spec: vmv1beta1.VMAuthSpec{ SelectAllByDefault: true, - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, UnauthorizedUserAccessSpec: &vmv1beta1.VMAuthUnauthorizedUserAccessSpec{ @@ -390,10 +388,8 @@ var _ = Describe("test vmauth Controller", Label("vm", "auth"), func() { &vmv1beta1.VMAuth{ Spec: vmv1beta1.VMAuthSpec{ SelectAllByDefault: true, - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ - ReplicaCount: ptr.To[int32](1), - }, - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ + ReplicaCount: ptr.To[int32](1), UseDefaultResources: ptr.To(false), }, UnauthorizedUserAccessSpec: &vmv1beta1.VMAuthUnauthorizedUserAccessSpec{ @@ -445,11 +441,9 @@ var _ = Describe("test vmauth Controller", Label("vm", "auth"), func() { &vmv1beta1.VMAuth{ Spec: vmv1beta1.VMAuthSpec{ SelectAllByDefault: true, - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ UseDefaultResources: ptr.To(false), - }, - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ - ReplicaCount: ptr.To[int32](2), + ReplicaCount: ptr.To[int32](2), }, PodDisruptionBudget: &vmv1beta1.EmbeddedPodDisruptionBudgetSpec{ MaxUnavailable: &intstr.IntOrString{IntVal: 1}, @@ -537,7 +531,7 @@ var _ = Describe("test vmauth Controller", Label("vm", "auth"), func() { &vmv1beta1.VMAuth{ Spec: vmv1beta1.VMAuthSpec{ SelectAllByDefault: true, - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, UnauthorizedUserAccessSpec: &vmv1beta1.VMAuthUnauthorizedUserAccessSpec{ @@ -624,7 +618,7 @@ var _ = Describe("test vmauth Controller", Label("vm", "auth"), func() { &vmv1beta1.VMAuth{ Spec: vmv1beta1.VMAuthSpec{ SelectAllByDefault: true, - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, UnauthorizedUserAccessSpec: &vmv1beta1.VMAuthUnauthorizedUserAccessSpec{ @@ -696,7 +690,7 @@ var _ = Describe("test vmauth Controller", Label("vm", "auth"), func() { &vmv1beta1.VMAuth{ Spec: vmv1beta1.VMAuthSpec{ SelectAllByDefault: true, - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, UnauthorizedUserAccessSpec: &vmv1beta1.VMAuthUnauthorizedUserAccessSpec{ @@ -732,7 +726,7 @@ var _ = Describe("test vmauth Controller", Label("vm", "auth"), func() { Name: nsn.Name, }, Spec: vmv1beta1.VMAuthSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: &initialReplicas, }, }, diff --git a/test/e2e/vmcluster_test.go b/test/e2e/vmcluster_test.go index dace800ad..56bc17b6b 100644 --- a/test/e2e/vmcluster_test.go +++ b/test/e2e/vmcluster_test.go @@ -61,17 +61,17 @@ var _ = Describe("e2e vmcluster", Label("vm", "cluster", "vmcluster"), func() { Spec: vmv1beta1.VMClusterSpec{ RetentionPeriod: "1", VMStorage: &vmv1beta1.VMStorage{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, VMSelect: &vmv1beta1.VMSelect{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, VMInsert: &vmv1beta1.VMInsert{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, @@ -151,16 +151,16 @@ var _ = Describe("e2e vmcluster", Label("vm", "cluster", "vmcluster"), func() { Spec: vmv1beta1.VMClusterSpec{ RetentionPeriod: "1", VMStorage: &vmv1beta1.VMStorage{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, VMSelect: &vmv1beta1.VMSelect{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, - VMInsert: &vmv1beta1.VMInsert{CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + VMInsert: &vmv1beta1.VMInsert{CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, @@ -176,12 +176,12 @@ var _ = Describe("e2e vmcluster", Label("vm", "cluster", "vmcluster"), func() { Spec: vmv1beta1.VMClusterSpec{ RetentionPeriod: "1", VMStorage: &vmv1beta1.VMStorage{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, VMSelect: &vmv1beta1.VMSelect{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, @@ -196,12 +196,12 @@ var _ = Describe("e2e vmcluster", Label("vm", "cluster", "vmcluster"), func() { Spec: vmv1beta1.VMClusterSpec{ RetentionPeriod: "1", VMStorage: &vmv1beta1.VMStorage{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, VMInsert: &vmv1beta1.VMInsert{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, @@ -217,31 +217,24 @@ var _ = Describe("e2e vmcluster", Label("vm", "cluster", "vmcluster"), func() { Spec: vmv1beta1.VMClusterSpec{ RetentionPeriod: "1", VMStorage: &vmv1beta1.VMStorage{ - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ UseStrictSecurity: ptr.To(true), UseDefaultResources: ptr.To(false), - }, - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ - ReplicaCount: ptr.To[int32](1), + ReplicaCount: ptr.To[int32](1), }, }, VMSelect: &vmv1beta1.VMSelect{ - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ UseStrictSecurity: ptr.To(true), UseDefaultResources: ptr.To(false), - }, - - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ - ReplicaCount: ptr.To[int32](1), + ReplicaCount: ptr.To[int32](1), }, }, VMInsert: &vmv1beta1.VMInsert{ - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ UseStrictSecurity: ptr.To(true), UseDefaultResources: ptr.To(false), - }, - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ - ReplicaCount: ptr.To[int32](1), + ReplicaCount: ptr.To[int32](1), }, }, }, @@ -275,11 +268,9 @@ var _ = Describe("e2e vmcluster", Label("vm", "cluster", "vmcluster"), func() { Spec: vmv1beta1.VMClusterSpec{ RetentionPeriod: "1", VMStorage: &vmv1beta1.VMStorage{ - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ UseDefaultResources: ptr.To(false), - }, - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ - ReplicaCount: ptr.To[int32](1), + ReplicaCount: ptr.To[int32](1), SecurityContext: &vmv1beta1.SecurityContext{ PodSecurityContext: &corev1.PodSecurityContext{ RunAsNonRoot: ptr.To(true), @@ -293,12 +284,9 @@ var _ = Describe("e2e vmcluster", Label("vm", "cluster", "vmcluster"), func() { }, }, VMSelect: &vmv1beta1.VMSelect{ - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ UseDefaultResources: ptr.To(false), - }, - - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ - ReplicaCount: ptr.To[int32](1), + ReplicaCount: ptr.To[int32](1), SecurityContext: &vmv1beta1.SecurityContext{ PodSecurityContext: &corev1.PodSecurityContext{ RunAsNonRoot: ptr.To(true), @@ -312,11 +300,9 @@ var _ = Describe("e2e vmcluster", Label("vm", "cluster", "vmcluster"), func() { }, }, VMInsert: &vmv1beta1.VMInsert{ - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ UseDefaultResources: ptr.To(false), - }, - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ - ReplicaCount: ptr.To[int32](1), + ReplicaCount: ptr.To[int32](1), SecurityContext: &vmv1beta1.SecurityContext{ PodSecurityContext: &corev1.PodSecurityContext{ RunAsNonRoot: ptr.To(true), @@ -360,7 +346,7 @@ var _ = Describe("e2e vmcluster", Label("vm", "cluster", "vmcluster"), func() { Spec: vmv1beta1.VMClusterSpec{ RetentionPeriod: "1", VMStorage: &vmv1beta1.VMStorage{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), ExtraArgs: map[string]string{ "httpListenAddr.useProxyProtocol": "true", @@ -368,7 +354,7 @@ var _ = Describe("e2e vmcluster", Label("vm", "cluster", "vmcluster"), func() { }, }, VMSelect: &vmv1beta1.VMSelect{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), ExtraArgs: map[string]string{ "httpListenAddr.useProxyProtocol": "true", @@ -376,7 +362,7 @@ var _ = Describe("e2e vmcluster", Label("vm", "cluster", "vmcluster"), func() { }, }, VMInsert: &vmv1beta1.VMInsert{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), ExtraArgs: map[string]string{ "httpListenAddr.useProxyProtocol": "true", @@ -399,17 +385,17 @@ var _ = Describe("e2e vmcluster", Label("vm", "cluster", "vmcluster"), func() { Enabled: true, }, VMStorage: &vmv1beta1.VMStorage{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, VMSelect: &vmv1beta1.VMSelect{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, VMInsert: &vmv1beta1.VMInsert{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, @@ -463,17 +449,17 @@ var _ = Describe("e2e vmcluster", Label("vm", "cluster", "vmcluster"), func() { Spec: vmv1beta1.VMClusterSpec{ RetentionPeriod: "1", VMStorage: &vmv1beta1.VMStorage{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(initialReplicas), }, }, VMSelect: &vmv1beta1.VMSelect{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(initialReplicas), }, }, VMInsert: &vmv1beta1.VMInsert{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(initialReplicas), }, }, @@ -605,17 +591,17 @@ var _ = Describe("e2e vmcluster", Label("vm", "cluster", "vmcluster"), func() { Spec: vmv1beta1.VMClusterSpec{ RetentionPeriod: "1", VMStorage: &vmv1beta1.VMStorage{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, VMSelect: &vmv1beta1.VMSelect{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, VMInsert: &vmv1beta1.VMInsert{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, @@ -647,18 +633,19 @@ var _ = Describe("e2e vmcluster", Label("vm", "cluster", "vmcluster"), func() { Spec: vmv1beta1.VMClusterSpec{ RetentionPeriod: "1", VMStorage: &vmv1beta1.VMStorage{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, VMSelect: &vmv1beta1.VMSelect{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, - VMInsert: &vmv1beta1.VMInsert{CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ - ReplicaCount: ptr.To[int32](1), - }, + VMInsert: &vmv1beta1.VMInsert{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ + ReplicaCount: ptr.To[int32](1), + }, }, }, }, @@ -719,17 +706,17 @@ var _ = Describe("e2e vmcluster", Label("vm", "cluster", "vmcluster"), func() { Spec: vmv1beta1.VMClusterSpec{ RetentionPeriod: "1", VMStorage: &vmv1beta1.VMStorage{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, VMSelect: &vmv1beta1.VMSelect{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, VMInsert: &vmv1beta1.VMInsert{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, @@ -763,18 +750,19 @@ var _ = Describe("e2e vmcluster", Label("vm", "cluster", "vmcluster"), func() { Spec: vmv1beta1.VMClusterSpec{ RetentionPeriod: "1", VMStorage: &vmv1beta1.VMStorage{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, VMSelect: &vmv1beta1.VMSelect{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, - VMInsert: &vmv1beta1.VMInsert{CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ - ReplicaCount: ptr.To[int32](1), - }, + VMInsert: &vmv1beta1.VMInsert{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ + ReplicaCount: ptr.To[int32](1), + }, }, }, }, @@ -794,18 +782,19 @@ var _ = Describe("e2e vmcluster", Label("vm", "cluster", "vmcluster"), func() { Spec: vmv1beta1.VMClusterSpec{ RetentionPeriod: "1", VMStorage: &vmv1beta1.VMStorage{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, VMSelect: &vmv1beta1.VMSelect{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, - VMInsert: &vmv1beta1.VMInsert{CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ - ReplicaCount: ptr.To[int32](1), - }, + VMInsert: &vmv1beta1.VMInsert{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ + ReplicaCount: ptr.To[int32](1), + }, }, }, }, @@ -836,18 +825,19 @@ var _ = Describe("e2e vmcluster", Label("vm", "cluster", "vmcluster"), func() { Spec: vmv1beta1.VMClusterSpec{ RetentionPeriod: "1", VMStorage: &vmv1beta1.VMStorage{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, VMSelect: &vmv1beta1.VMSelect{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, - VMInsert: &vmv1beta1.VMInsert{CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ - ReplicaCount: ptr.To[int32](1), - }, + VMInsert: &vmv1beta1.VMInsert{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ + ReplicaCount: ptr.To[int32](1), + }, }, }, }, @@ -873,11 +863,9 @@ var _ = Describe("e2e vmcluster", Label("vm", "cluster", "vmcluster"), func() { testStep{ modify: func(cr *vmv1beta1.VMCluster) { cr.Spec.VMSelect = &vmv1beta1.VMSelect{ - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ UseDefaultResources: ptr.To(false), - }, - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ - ReplicaCount: ptr.To[int32](1), + ReplicaCount: ptr.To[int32](1), }, } }, @@ -893,18 +881,19 @@ var _ = Describe("e2e vmcluster", Label("vm", "cluster", "vmcluster"), func() { Spec: vmv1beta1.VMClusterSpec{ RetentionPeriod: "1", VMStorage: &vmv1beta1.VMStorage{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, VMSelect: &vmv1beta1.VMSelect{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, - VMInsert: &vmv1beta1.VMInsert{CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ - ReplicaCount: ptr.To[int32](1), - }, + VMInsert: &vmv1beta1.VMInsert{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ + ReplicaCount: ptr.To[int32](1), + }, }, }, }, @@ -940,19 +929,15 @@ var _ = Describe("e2e vmcluster", Label("vm", "cluster", "vmcluster"), func() { testStep{ modify: func(cr *vmv1beta1.VMCluster) { cr.Spec.VMStorage = &vmv1beta1.VMStorage{ - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ UseDefaultResources: ptr.To(false), - }, - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ - ReplicaCount: ptr.To[int32](1), + ReplicaCount: ptr.To[int32](1), }, } cr.Spec.VMInsert = &vmv1beta1.VMInsert{ - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ UseDefaultResources: ptr.To(false), - }, - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ - ReplicaCount: ptr.To[int32](1), + ReplicaCount: ptr.To[int32](1), }, } }, @@ -971,7 +956,7 @@ var _ = Describe("e2e vmcluster", Label("vm", "cluster", "vmcluster"), func() { Spec: vmv1beta1.VMClusterSpec{ RetentionPeriod: "1", VMStorage: &vmv1beta1.VMStorage{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, ServiceSpec: &vmv1beta1.AdditionalServiceSpec{ @@ -991,7 +976,7 @@ var _ = Describe("e2e vmcluster", Label("vm", "cluster", "vmcluster"), func() { }, }, VMSelect: &vmv1beta1.VMSelect{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, ServiceSpec: &vmv1beta1.AdditionalServiceSpec{ @@ -1068,18 +1053,19 @@ var _ = Describe("e2e vmcluster", Label("vm", "cluster", "vmcluster"), func() { RetentionPeriod: "1", ImagePullSecrets: nil, VMStorage: &vmv1beta1.VMStorage{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, VMSelect: &vmv1beta1.VMSelect{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, - VMInsert: &vmv1beta1.VMInsert{CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ - ReplicaCount: ptr.To[int32](1), - }, + VMInsert: &vmv1beta1.VMInsert{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ + ReplicaCount: ptr.To[int32](1), + }, }, }, }, @@ -1118,18 +1104,19 @@ var _ = Describe("e2e vmcluster", Label("vm", "cluster", "vmcluster"), func() { Enabled: false, }, VMStorage: &vmv1beta1.VMStorage{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, VMSelect: &vmv1beta1.VMSelect{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, - VMInsert: &vmv1beta1.VMInsert{CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ - ReplicaCount: ptr.To[int32](1), - }, + VMInsert: &vmv1beta1.VMInsert{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ + ReplicaCount: ptr.To[int32](1), + }, }, }, }, @@ -1231,17 +1218,17 @@ up{baz="bar"} 123 Enabled: false, }, VMStorage: &vmv1beta1.VMStorage{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, VMSelect: &vmv1beta1.VMSelect{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, VMInsert: &vmv1beta1.VMInsert{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, @@ -1361,24 +1348,25 @@ up{baz="bar"} 123 RequestsLoadBalancer: vmv1beta1.VMAuthLoadBalancer{ Enabled: true, Spec: vmv1beta1.VMAuthLoadBalancerSpec{ - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ Port: "8431", }, }, }, VMStorage: &vmv1beta1.VMStorage{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, VMSelect: &vmv1beta1.VMSelect{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](2), }, }, - VMInsert: &vmv1beta1.VMInsert{CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ - ReplicaCount: ptr.To[int32](1), - }, + VMInsert: &vmv1beta1.VMInsert{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ + ReplicaCount: ptr.To[int32](1), + }, }, }, }, @@ -1475,7 +1463,7 @@ up{baz="bar"} 123 RequestsLoadBalancer: vmv1beta1.VMAuthLoadBalancer{Enabled: true}, RetentionPeriod: "1", VMStorage: &vmv1beta1.VMStorage{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, Storage: &vmv1beta1.StorageSpec{ @@ -1496,12 +1484,12 @@ up{baz="bar"} 123 }, }, VMSelect: &vmv1beta1.VMSelect{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, VMInsert: &vmv1beta1.VMInsert{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, @@ -1529,18 +1517,19 @@ up{baz="bar"} 123 RequestsLoadBalancer: vmv1beta1.VMAuthLoadBalancer{Enabled: true}, RetentionPeriod: "1", VMStorage: &vmv1beta1.VMStorage{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, VMSelect: &vmv1beta1.VMSelect{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, - VMInsert: &vmv1beta1.VMInsert{CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ - ReplicaCount: ptr.To[int32](1), - }, + VMInsert: &vmv1beta1.VMInsert{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ + ReplicaCount: ptr.To[int32](1), + }, }, }, }, @@ -1658,7 +1647,7 @@ up{baz="bar"} 123 Spec: vmv1beta1.VMClusterSpec{ RetentionPeriod: "1", VMStorage: &vmv1beta1.VMStorage{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](4), }, PodMetadata: &vmv1beta1.EmbeddedObjectMetadata{ @@ -1669,12 +1658,12 @@ up{baz="bar"} 123 }, }, VMSelect: &vmv1beta1.VMSelect{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, VMInsert: &vmv1beta1.VMInsert{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, @@ -1714,7 +1703,7 @@ up{baz="bar"} 123 Spec: vmv1beta1.VMClusterSpec{ RetentionPeriod: "1", VMStorage: &vmv1beta1.VMStorage{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](4), }, PodMetadata: &vmv1beta1.EmbeddedObjectMetadata{ @@ -1725,12 +1714,12 @@ up{baz="bar"} 123 }, }, VMSelect: &vmv1beta1.VMSelect{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, VMInsert: &vmv1beta1.VMInsert{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, @@ -1774,7 +1763,7 @@ up{baz="bar"} 123 Spec: vmv1beta1.VMClusterSpec{ RetentionPeriod: "1", VMStorage: &vmv1beta1.VMStorage{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](4), }, PodMetadata: &vmv1beta1.EmbeddedObjectMetadata{ @@ -1788,12 +1777,12 @@ up{baz="bar"} 123 }, }, VMSelect: &vmv1beta1.VMSelect{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, VMInsert: &vmv1beta1.VMInsert{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, diff --git a/test/e2e/vmdistributed_test.go b/test/e2e/vmdistributed_test.go index 606c8182f..6ad75f1c2 100644 --- a/test/e2e/vmdistributed_test.go +++ b/test/e2e/vmdistributed_test.go @@ -22,22 +22,22 @@ import ( ) func genVMClusterSpec(opts ...func(*vmv1beta1.VMClusterSpec)) vmv1beta1.VMClusterSpec { - commonApplicationDeploymentParams := vmv1beta1.CommonApplicationDeploymentParams{ + commonAppsParams := vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), } - noReplicas := vmv1beta1.CommonApplicationDeploymentParams{ + noReplicas := vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](0), } s := vmv1beta1.VMClusterSpec{ VMSelect: &vmv1beta1.VMSelect{ - CommonApplicationDeploymentParams: commonApplicationDeploymentParams, + CommonAppsParams: commonAppsParams, }, VMInsert: &vmv1beta1.VMInsert{ - CommonApplicationDeploymentParams: noReplicas, + CommonAppsParams: noReplicas, }, VMStorage: &vmv1beta1.VMStorage{ - CommonApplicationDeploymentParams: commonApplicationDeploymentParams, + CommonAppsParams: commonAppsParams, }, } for _, opt := range opts { @@ -67,7 +67,7 @@ func createVMClusters(ctx context.Context, wg *sync.WaitGroup, k8sClient client. func genVMAgentSpec() vmv1beta1.VMAgentSpec { return vmv1beta1.VMAgentSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, RemoteWrite: []vmv1beta1.VMAgentRemoteWriteSpec{ @@ -103,7 +103,7 @@ func createVMAuth(ctx context.Context, wg *sync.WaitGroup, k8sClient client.Clie Name: name, }, Spec: vmv1beta1.VMAuthSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, UserSelector: &metav1.LabelSelector{ @@ -190,7 +190,7 @@ var _ = Describe("e2e VMDistributed", Label("vm", "vmdistributed"), func() { UpdatePause: &metav1.Duration{Duration: 1 * time.Second}, VMAgent: vmv1alpha1.VMDistributedZoneAgent{ Spec: vmv1alpha1.VMDistributedZoneAgentSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](2), }, }, @@ -373,7 +373,7 @@ var _ = Describe("e2e VMDistributed", Label("vm", "vmdistributed"), func() { VMAuth: vmv1alpha1.VMDistributedAuth{ Name: nsn.Name, Spec: vmv1beta1.VMAuthSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, @@ -395,7 +395,7 @@ var _ = Describe("e2e VMDistributed", Label("vm", "vmdistributed"), func() { var createdVMAuth vmv1beta1.VMAuth nsn = types.NamespacedName{Name: nsn.Name, Namespace: namespace} Expect(k8sClient.Get(ctx, nsn, &createdVMAuth)).ToNot(HaveOccurred()) - Expect(createdVMAuth.Spec.CommonApplicationDeploymentParams.ReplicaCount).To(HaveValue(Equal(int32(1)))) + Expect(createdVMAuth.Spec.CommonAppsParams.ReplicaCount).To(HaveValue(Equal(int32(1)))) By("verifying VMAuth has correct owner reference") Expect(createdVMAuth.GetOwnerReferences()).To(HaveLen(1)) @@ -819,7 +819,7 @@ var _ = Describe("e2e VMDistributed", Label("vm", "vmdistributed"), func() { VMInsert: cr.Spec.Zones[0].VMCluster.Spec.VMInsert, VMSelect: cr.Spec.Zones[0].VMCluster.Spec.VMSelect, VMStorage: &vmv1beta1.VMStorage{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](initialReplicas + 1), }, }, @@ -1114,7 +1114,7 @@ var _ = Describe("e2e VMDistributed", Label("vm", "vmdistributed"), func() { VMAuth: vmv1alpha1.VMDistributedAuth{ Name: nsn.Name, Spec: vmv1beta1.VMAuthSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, diff --git a/test/e2e/vmsingle_test.go b/test/e2e/vmsingle_test.go index 1998e5743..ace347f2f 100644 --- a/test/e2e/vmsingle_test.go +++ b/test/e2e/vmsingle_test.go @@ -76,7 +76,7 @@ var _ = Describe("test vmsingle Controller", Label("vm", "single"), func() { Name: nsn.Name, }, Spec: vmv1beta1.VMSingleSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, RetentionPeriod: "1", @@ -142,7 +142,7 @@ var _ = Describe("test vmsingle Controller", Label("vm", "single"), func() { Namespace: namespace, }, Spec: vmv1beta1.VMSingleSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, RetentionPeriod: "1", @@ -180,13 +180,11 @@ var _ = Describe("test vmsingle Controller", Label("vm", "single"), func() { Namespace: namespace, }, Spec: vmv1beta1.VMSingleSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), Volumes: []corev1.Volume{ {Name: "backup", VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{}}}, }, - }, - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ UseDefaultResources: ptr.To(false), }, VMBackup: &vmv1beta1.VMBackup{ @@ -229,10 +227,8 @@ var _ = Describe("test vmsingle Controller", Label("vm", "single"), func() { Namespace: namespace, }, Spec: vmv1beta1.VMSingleSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ - ReplicaCount: ptr.To[int32](1), - }, - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ + ReplicaCount: ptr.To[int32](1), UseStrictSecurity: ptr.To(true), }, RetentionPeriod: "1", @@ -262,10 +258,8 @@ var _ = Describe("test vmsingle Controller", Label("vm", "single"), func() { Namespace: namespace, }, Spec: vmv1beta1.VMSingleSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ - ReplicaCount: ptr.To[int32](1), - }, - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ + ReplicaCount: ptr.To[int32](1), UseStrictSecurity: ptr.To(false), }, RetentionPeriod: "1", @@ -295,10 +289,8 @@ var _ = Describe("test vmsingle Controller", Label("vm", "single"), func() { Namespace: namespace, }, Spec: vmv1beta1.VMSingleSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ - ReplicaCount: ptr.To[int32](1), - }, - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ + ReplicaCount: ptr.To[int32](1), UseStrictSecurity: ptr.To(false), }, RetentionPeriod: "1", @@ -321,7 +313,7 @@ var _ = Describe("test vmsingle Controller", Label("vm", "single"), func() { Namespace: namespace, }, Spec: vmv1beta1.VMSingleSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), Volumes: []corev1.Volume{ { @@ -349,8 +341,6 @@ var _ = Describe("test vmsingle Controller", Label("vm", "single"), func() { MountPath: "/opt/unused/mountpoint", }, }, - }, - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ UseStrictSecurity: ptr.To(false), }, RetentionPeriod: "1", @@ -388,7 +378,7 @@ var _ = Describe("test vmsingle Controller", Label("vm", "single"), func() { Spec: vmv1beta1.VMSingleSpec{ RemovePvcAfterDelete: true, RetentionPeriod: "10", - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, @@ -528,7 +518,7 @@ var _ = Describe("test vmsingle Controller", Label("vm", "single"), func() { Name: nsn.Name, }, Spec: vmv1beta1.VMSingleSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: &initialReplicas, }, RetentionPeriod: "1", diff --git a/test/e2e/vtcluster_test.go b/test/e2e/vtcluster_test.go index 51e5f13a2..f3ce6443f 100644 --- a/test/e2e/vtcluster_test.go +++ b/test/e2e/vtcluster_test.go @@ -61,7 +61,7 @@ var _ = Describe("test vtcluster Controller", Label("vt", "cluster", "vtcluster" Spec: vmv1.VTClusterSpec{ Storage: &vmv1.VTStorage{ RetentionPeriod: "1", - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), ExtraArgs: map[string]string{ "httpListenAddr.useProxyProtocol": "true", @@ -69,7 +69,7 @@ var _ = Describe("test vtcluster Controller", Label("vt", "cluster", "vtcluster" }, }, Select: &vmv1.VTSelect{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), ExtraArgs: map[string]string{ "httpListenAddr.useProxyProtocol": "true", @@ -77,7 +77,7 @@ var _ = Describe("test vtcluster Controller", Label("vt", "cluster", "vtcluster" }, }, Insert: &vmv1.VTInsert{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), ExtraArgs: map[string]string{ "httpListenAddr.useProxyProtocol": "true", @@ -99,7 +99,7 @@ var _ = Describe("test vtcluster Controller", Label("vt", "cluster", "vtcluster" Select: &vmv1.VTSelect{}, Storage: &vmv1.VTStorage{ RetentionPeriod: "1", - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, @@ -286,7 +286,7 @@ var _ = Describe("test vtcluster Controller", Label("vt", "cluster", "vtcluster" modify: func(cr *vmv1.VTCluster) { By("upscaling vtselect, removing vtinsert", func() { cr.Spec.Select = &vmv1.VTSelect{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(2)), }, } @@ -312,12 +312,12 @@ var _ = Describe("test vtcluster Controller", Label("vt", "cluster", "vtcluster" modify: func(cr *vmv1.VTCluster) { By("downscaling all components to 0 replicas", func() { cr.Spec.Select = &vmv1.VTSelect{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(0)), }, } cr.Spec.Insert = &vmv1.VTInsert{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(0)), }, } diff --git a/test/e2e/vtsingle_test.go b/test/e2e/vtsingle_test.go index 394c3ce3d..d0207228f 100644 --- a/test/e2e/vtsingle_test.go +++ b/test/e2e/vtsingle_test.go @@ -61,10 +61,8 @@ var _ = Describe("test vtsingle Controller", Label("vt", "single", "vtsingle"), Namespace: namespace, }, Spec: vmv1.VTSingleSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ - ReplicaCount: ptr.To[int32](1), - }, - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ + ReplicaCount: ptr.To[int32](1), UseStrictSecurity: ptr.To(true), }, RetentionPeriod: "1", @@ -93,10 +91,8 @@ var _ = Describe("test vtsingle Controller", Label("vt", "single", "vtsingle"), Namespace: namespace, }, Spec: vmv1.VTSingleSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ - ReplicaCount: ptr.To[int32](1), - }, - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ + ReplicaCount: ptr.To[int32](1), UseStrictSecurity: ptr.To(false), }, RetentionPeriod: "1", @@ -117,7 +113,7 @@ var _ = Describe("test vtsingle Controller", Label("vt", "single", "vtsingle"), Namespace: namespace, }, Spec: vmv1.VTSingleSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), Volumes: []corev1.Volume{ { @@ -139,8 +135,6 @@ var _ = Describe("test vtsingle Controller", Label("vt", "single", "vtsingle"), MountPath: "/opt/unused/mountpoint", }, }, - }, - CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ UseStrictSecurity: ptr.To(false), }, RetentionPeriod: "1", @@ -165,7 +159,7 @@ var _ = Describe("test vtsingle Controller", Label("vt", "single", "vtsingle"), Namespace: namespace, }, Spec: vmv1.VTSingleSpec{ - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), ExtraArgs: map[string]string{ "httpListenAddr.useProxyProtocol": "true", @@ -184,7 +178,7 @@ var _ = Describe("test vtsingle Controller", Label("vt", "single", "vtsingle"), }, Spec: vmv1.VTSingleSpec{ RetentionPeriod: "10", - CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](1), }, }, From 4b75b15bfd5030182e9a0bc71bfd643fca7d66e8 Mon Sep 17 00:00:00 2001 From: Vadim Rutkovsky Date: Thu, 5 Mar 2026 11:30:41 +0100 Subject: [PATCH 04/22] fix: set maxUnavailable to 1 when its 0 to avoid an infinite loop (#1932) --- .../operator/factory/reconcile/statefulset.go | 4 + .../factory/reconcile/statefulset_test.go | 139 ++++++++++++++++++ test/e2e/vlcluster_test.go | 6 +- 3 files changed, 146 insertions(+), 3 deletions(-) diff --git a/internal/controller/operator/factory/reconcile/statefulset.go b/internal/controller/operator/factory/reconcile/statefulset.go index d0115d35a..25560db24 100644 --- a/internal/controller/operator/factory/reconcile/statefulset.go +++ b/internal/controller/operator/factory/reconcile/statefulset.go @@ -303,6 +303,10 @@ func performRollingUpdateOnSts(ctx context.Context, rclient client.Client, obj * } // perform update for not updated pods in batches according to podMaxUnavailable + // clamp to 1 to avoid an infinite loop when percentage-based maxUnavailable rounds to 0 + if o.maxUnavailable == 0 { + o.maxUnavailable = 1 + } for batchStart := 0; batchStart < len(podsForUpdate); batchStart += o.maxUnavailable { var batch []corev1.Pod diff --git a/internal/controller/operator/factory/reconcile/statefulset_test.go b/internal/controller/operator/factory/reconcile/statefulset_test.go index acd910ea0..dfd3b585e 100644 --- a/internal/controller/operator/factory/reconcile/statefulset_test.go +++ b/internal/controller/operator/factory/reconcile/statefulset_test.go @@ -611,6 +611,145 @@ func Test_performRollingUpdateOnSts(t *testing.T) { }, }) + // maxUnavailable=0 with 1 pod + f(opts{ + sts: &appsv1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "vmselect-sts", + Namespace: "default", + }, + Status: appsv1.StatefulSetStatus{ + CurrentRevision: "rev1", + UpdateRevision: "rev2", + }, + }, + opts: rollingUpdateOpts{ + selector: map[string]string{"app": "vmselect"}, + maxUnavailable: 0, + }, + predefinedObjects: []runtime.Object{ + &appsv1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "vmselect-sts", + Namespace: "default", + Labels: map[string]string{"app": "vmselect"}, + }, + Spec: appsv1.StatefulSetSpec{Replicas: ptr.To(int32(1))}, + Status: appsv1.StatefulSetStatus{ + CurrentRevision: "rev1", + UpdateRevision: "rev2", + }, + }, + &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "vmselect-sts-0", + Namespace: "default", + Labels: map[string]string{"app": "vmselect", podRevisionLabel: "rev1"}, + OwnerReferences: []metav1.OwnerReference{{ + Kind: "StatefulSet", + }}, + }, + Status: corev1.PodStatus{ + Phase: corev1.PodRunning, + Conditions: []corev1.PodCondition{ + { + Type: corev1.PodReady, + Status: "True", + }, + }, + }, + }, + }, + actions: map[string][]string{ + "vmselect-sts-0": {"Evict", "Get"}, + }, + }) + + // maxUnavailable=0 with 3 pods + f(opts{ + sts: &appsv1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "vmselect-sts", + Namespace: "default", + }, + Status: appsv1.StatefulSetStatus{ + CurrentRevision: "rev1", + UpdateRevision: "rev2", + }, + }, + opts: rollingUpdateOpts{ + selector: map[string]string{"app": "vmselect"}, + maxUnavailable: 0, + }, + predefinedObjects: []runtime.Object{ + &appsv1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "vmselect-sts", + Namespace: "default", + Labels: map[string]string{"app": "vmselect"}, + }, + Spec: appsv1.StatefulSetSpec{Replicas: ptr.To(int32(3))}, + Status: appsv1.StatefulSetStatus{ + CurrentRevision: "rev1", + UpdateRevision: "rev2", + }, + }, + &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "vmselect-sts-0", + Namespace: "default", + Labels: map[string]string{"app": "vmselect", podRevisionLabel: "rev1"}, + OwnerReferences: []metav1.OwnerReference{{ + Kind: "StatefulSet", + }}, + }, + Status: corev1.PodStatus{ + Phase: corev1.PodRunning, + Conditions: []corev1.PodCondition{ + {Type: corev1.PodReady, Status: "True"}, + }, + }, + }, + &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "vmselect-sts-1", + Namespace: "default", + Labels: map[string]string{"app": "vmselect", podRevisionLabel: "rev1"}, + OwnerReferences: []metav1.OwnerReference{{ + Kind: "StatefulSet", + }}, + }, + Status: corev1.PodStatus{ + Phase: corev1.PodRunning, + Conditions: []corev1.PodCondition{ + {Type: corev1.PodReady, Status: "True"}, + }, + }, + }, + &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "vmselect-sts-2", + Namespace: "default", + Labels: map[string]string{"app": "vmselect", podRevisionLabel: "rev1"}, + OwnerReferences: []metav1.OwnerReference{{ + Kind: "StatefulSet", + }}, + }, + Status: corev1.PodStatus{ + Phase: corev1.PodRunning, + Conditions: []corev1.PodCondition{ + {Type: corev1.PodReady, Status: "True"}, + }, + }, + }, + }, + actions: map[string][]string{ + "vmselect-sts-0": {"Evict", "Get"}, + "vmselect-sts-1": {"Evict", "Get"}, + "vmselect-sts-2": {"Evict", "Get"}, + }, + }) + // rolling update is timeout f(opts{ sts: &appsv1.StatefulSet{ diff --git a/test/e2e/vlcluster_test.go b/test/e2e/vlcluster_test.go index 7c46851b9..6f47379b8 100644 --- a/test/e2e/vlcluster_test.go +++ b/test/e2e/vlcluster_test.go @@ -62,7 +62,7 @@ var _ = Describe("test vlcluster Controller", Label("vl", "cluster", "vlcluster" Expect(k8sClient.Create(ctx, cr)).ToNot(HaveOccurred()) Eventually(func() error { return expectObjectStatusOperational(ctx, k8sClient, &vmv1.VLCluster{}, nsn) - }, eventualDeploymentAppReadyTimeout).ShouldNot(HaveOccurred()) + }, eventualStatefulsetAppReadyTimeout).ShouldNot(HaveOccurred()) if verify != nil { var created vmv1.VLCluster Expect(k8sClient.Get(ctx, nsn, &created)).ToNot(HaveOccurred()) @@ -149,7 +149,7 @@ var _ = Describe("test vlcluster Controller", Label("vl", "cluster", "vlcluster" Expect(k8sClient.Create(ctx, initCR)).ToNot(HaveOccurred()) Eventually(func() error { return expectObjectStatusOperational(ctx, k8sClient, &vmv1.VLCluster{}, nsn) - }, eventualDeploymentAppReadyTimeout).ShouldNot(HaveOccurred()) + }, eventualStatefulsetAppReadyTimeout).ShouldNot(HaveOccurred()) for _, step := range steps { if step.setup != nil { @@ -162,7 +162,7 @@ var _ = Describe("test vlcluster Controller", Label("vl", "cluster", "vlcluster" Expect(k8sClient.Update(ctx, &toUpdate)).ToNot(HaveOccurred()) Eventually(func() error { return expectObjectStatusOperational(ctx, k8sClient, &vmv1.VLCluster{}, nsn) - }, eventualDeploymentAppReadyTimeout).ShouldNot(HaveOccurred()) + }, eventualStatefulsetAppReadyTimeout).ShouldNot(HaveOccurred()) var updated vmv1.VLCluster Expect(k8sClient.Get(ctx, nsn, &updated)).ToNot(HaveOccurred()) From df38d2c32314bebd21b41d737278df46d44e1b6f Mon Sep 17 00:00:00 2001 From: Andrii Chubatiuk Date: Fri, 20 Mar 2026 15:49:16 +0200 Subject: [PATCH 05/22] ported fixes from PRs #1980 and #1971 --- api/operator/v1/vmanomaly_types.go | 6 +- api/operator/v1/zz_generated.deepcopy.go | 2 +- api/operator/v1alpha1/vmdistributed_types.go | 11 ++ .../v1alpha1/zz_generated.deepcopy.go | 5 + api/operator/v1beta1/vmagent_types.go | 21 +++- api/operator/v1beta1/vmcluster_types.go | 7 +- api/operator/v1beta1/zz_generated.deepcopy.go | 10 +- config/crd/overlay/crd.descriptionless.yaml | 26 ++++ config/crd/overlay/crd.yaml | 47 +++++++ ...etrics-operator.clusterserviceversion.yaml | 7 -- docs/api.md | 12 +- .../operator/factory/build/shard.go | 17 ++- .../operator/factory/build/shard_test.go | 6 +- .../operator/factory/reconcile/deploy.go | 17 ++- .../operator/factory/reconcile/deploy_test.go | 34 ++++- .../operator/factory/reconcile/statefulset.go | 41 +++--- .../reconcile/statefulset_pvc_expand.go | 2 - .../reconcile/statefulset_pvc_expand_test.go | 2 +- .../factory/reconcile/statefulset_test.go | 118 +++++++++++++++++- .../operator/factory/vlagent/vlagent.go | 7 +- .../operator/factory/vlcluster/vlinsert.go | 9 +- .../operator/factory/vlcluster/vlselect.go | 9 +- .../operator/factory/vlcluster/vlstorage.go | 14 ++- .../operator/factory/vlcluster/vmauth_lb.go | 2 +- .../operator/factory/vlsingle/vlsingle.go | 2 +- .../operator/factory/vmagent/vmagent.go | 17 ++- .../operator/factory/vmagent/vmagent_test.go | 4 +- .../operator/factory/vmalert/vmalert.go | 2 +- .../factory/vmalertmanager/alertmanager.go | 7 +- .../operator/factory/vmanomaly/statefulset.go | 20 ++- .../operator/factory/vmauth/vmauth.go | 9 +- .../operator/factory/vmcluster/vmcluster.go | 43 ++++--- .../operator/factory/vmsingle/vmsingle.go | 2 +- .../operator/factory/vtcluster/insert.go | 9 +- .../operator/factory/vtcluster/select.go | 9 +- .../operator/factory/vtcluster/storage.go | 14 ++- .../operator/factory/vtcluster/vmauth_lb.go | 2 +- .../operator/factory/vtsingle/vtsingle.go | 2 +- test/e2e/vmagent_test.go | 2 +- test/e2e/vmanomaly_test.go | 2 +- 40 files changed, 423 insertions(+), 155 deletions(-) diff --git a/api/operator/v1/vmanomaly_types.go b/api/operator/v1/vmanomaly_types.go index 6203c39f5..1de93d1f2 100644 --- a/api/operator/v1/vmanomaly_types.go +++ b/api/operator/v1/vmanomaly_types.go @@ -55,7 +55,7 @@ type VMAnomalySpec struct { // in this case operator will use 1 sts per shard with // replicas count according to spec.replicas. // +optional - ShardCount *int `json:"shardCount,omitempty"` + ShardCount *int32 `json:"shardCount,omitempty"` // PodDisruptionBudget created by operator // +optional PodDisruptionBudget *vmv1beta1.EmbeddedPodDisruptionBudgetSpec `json:"podDisruptionBudget,omitempty"` @@ -306,7 +306,7 @@ func (cr *VMAnomaly) GetStatus() *VMAnomalyStatus { func (cr *VMAnomaly) DefaultStatusFields(vs *VMAnomalyStatus) { var shardCnt int32 if cr.IsSharded() { - shardCnt = int32(*cr.Spec.ShardCount) + shardCnt = *cr.Spec.ShardCount } vs.Shards = shardCnt } @@ -441,7 +441,7 @@ func (cr *VMAnomaly) IsSharded() bool { } // GetShardCount returns shard count for vmanomaly -func (cr *VMAnomaly) GetShardCount() int { +func (cr *VMAnomaly) GetShardCount() int32 { if !cr.IsSharded() { return 1 } diff --git a/api/operator/v1/zz_generated.deepcopy.go b/api/operator/v1/zz_generated.deepcopy.go index 86e6fee5f..c8c858a5b 100644 --- a/api/operator/v1/zz_generated.deepcopy.go +++ b/api/operator/v1/zz_generated.deepcopy.go @@ -1213,7 +1213,7 @@ func (in *VMAnomalySpec) DeepCopyInto(out *VMAnomalySpec) { } if in.ShardCount != nil { in, out := &in.ShardCount, &out.ShardCount - *out = new(int) + *out = new(int32) **out = **in } if in.PodDisruptionBudget != nil { diff --git a/api/operator/v1alpha1/vmdistributed_types.go b/api/operator/v1alpha1/vmdistributed_types.go index 362d6386b..beec63041 100644 --- a/api/operator/v1alpha1/vmdistributed_types.go +++ b/api/operator/v1alpha1/vmdistributed_types.go @@ -197,6 +197,10 @@ type VMDistributedZoneAgentSpec struct { // set it to RollingUpdate for disabling operator statefulSet rollingUpdate // +optional StatefulRollingUpdateStrategy appsv1.StatefulSetUpdateStrategyType `json:"statefulRollingUpdateStrategy,omitempty"` + // StatefulRollingUpdateStrategyBehavior defines customized behavior for rolling updates. + // It applies if the RollingUpdateStrategy is set to OnDelete, which is the default. + // +optional + StatefulRollingUpdateStrategyBehavior *vmv1beta1.StatefulSetUpdateStrategyBehavior `json:"statefulRollingUpdateStrategyBehavior,omitempty"` // PersistentVolumeClaimRetentionPolicy allows configuration of PVC retention policy // +optional PersistentVolumeClaimRetentionPolicy *appsv1.StatefulSetPersistentVolumeClaimRetentionPolicy `json:"persistentVolumeClaimRetentionPolicy,omitempty"` @@ -458,6 +462,13 @@ func (cr *VMDistributed) Validate() error { } agents[agentName] = struct{}{} } + if zone.VMAgent.Spec.StatefulMode { + if zone.VMAgent.Spec.StatefulRollingUpdateStrategyBehavior != nil { + if err := zone.VMAgent.Spec.StatefulRollingUpdateStrategyBehavior.Validate(); err != nil { + return fmt.Errorf("spec.zones[%d].vmagent.spec.statefulRollingUpdateStrategyBehavior: %w", i, err) + } + } + } if zone.VMCluster.Spec.VMInsert == nil && !hasCommonVMInsert { return fmt.Errorf("either zoneCommon.vmcluster.spec.vminsert or spec.zones[%d].vmcluster.spec.vminsert is required", i) } diff --git a/api/operator/v1alpha1/zz_generated.deepcopy.go b/api/operator/v1alpha1/zz_generated.deepcopy.go index 0ca17553b..4e4f4a8c4 100644 --- a/api/operator/v1alpha1/zz_generated.deepcopy.go +++ b/api/operator/v1alpha1/zz_generated.deepcopy.go @@ -225,6 +225,11 @@ func (in *VMDistributedZoneAgentSpec) DeepCopyInto(out *VMDistributedZoneAgentSp *out = new(v1beta1.StorageSpec) (*in).DeepCopyInto(*out) } + if in.StatefulRollingUpdateStrategyBehavior != nil { + in, out := &in.StatefulRollingUpdateStrategyBehavior, &out.StatefulRollingUpdateStrategyBehavior + *out = new(v1beta1.StatefulSetUpdateStrategyBehavior) + (*in).DeepCopyInto(*out) + } if in.PersistentVolumeClaimRetentionPolicy != nil { in, out := &in.PersistentVolumeClaimRetentionPolicy, &out.PersistentVolumeClaimRetentionPolicy *out = new(appsv1.StatefulSetPersistentVolumeClaimRetentionPolicy) diff --git a/api/operator/v1beta1/vmagent_types.go b/api/operator/v1beta1/vmagent_types.go index 9fd525b38..cc05a5f29 100644 --- a/api/operator/v1beta1/vmagent_types.go +++ b/api/operator/v1beta1/vmagent_types.go @@ -66,7 +66,7 @@ type VMAgentSpec struct { // replicas count according to spec.replicas, // see [here](https://docs.victoriametrics.com/victoriametrics/vmagent/#scraping-big-number-of-targets) // +optional - ShardCount *int `json:"shardCount,omitempty"` + ShardCount *int32 `json:"shardCount,omitempty"` // UpdateStrategy - overrides default update strategy. // works only for deployments, statefulset always use OnDelete. @@ -96,6 +96,10 @@ type VMAgentSpec struct { // set it to RollingUpdate for disabling operator statefulSet rollingUpdate // +optional StatefulRollingUpdateStrategy appsv1.StatefulSetUpdateStrategyType `json:"statefulRollingUpdateStrategy,omitempty"` + // StatefulRollingUpdateStrategyBehavior defines customized behavior for rolling updates. + // It applies if the RollingUpdateStrategy is set to OnDelete, which is the default. + // +optional + StatefulRollingUpdateStrategyBehavior *StatefulSetUpdateStrategyBehavior `json:"statefulRollingUpdateStrategyBehavior,omitempty"` // PersistentVolumeClaimRetentionPolicy allows configuration of PVC retention policy // +optional PersistentVolumeClaimRetentionPolicy *appsv1.StatefulSetPersistentVolumeClaimRetentionPolicy `json:"persistentVolumeClaimRetentionPolicy,omitempty"` @@ -155,8 +159,15 @@ func (cr *VMAgent) Validate() error { } } } - if cr.Spec.DaemonSetMode && cr.Spec.StatefulMode { - return fmt.Errorf("daemonSetMode and statefulMode cannot be used in the same time") + if cr.Spec.StatefulMode { + if cr.Spec.DaemonSetMode { + return fmt.Errorf("daemonSetMode and statefulMode cannot be used in the same time") + } + if cr.Spec.StatefulRollingUpdateStrategyBehavior != nil { + if err := cr.Spec.StatefulRollingUpdateStrategyBehavior.Validate(); err != nil { + return fmt.Errorf("vmagent.spec.statefulRollingUpdateStrategyBehavior: %w", err) + } + } } if cr.Spec.DaemonSetMode { if cr.Spec.PodDisruptionBudget != nil { @@ -202,7 +213,7 @@ func (cr *VMAgent) IsSharded() bool { } // GetShardCount returns shard count for vmagent -func (cr *VMAgent) GetShardCount() int { +func (cr *VMAgent) GetShardCount() int32 { if !cr.IsSharded() { return 1 } @@ -481,7 +492,7 @@ func (cr *VMAgent) DefaultStatusFields(vs *VMAgentStatus) { } var shardCnt int32 if cr.IsSharded() { - shardCnt = int32(*cr.Spec.ShardCount) + shardCnt = *cr.Spec.ShardCount } vs.Replicas = replicaCount vs.Shards = shardCnt diff --git a/api/operator/v1beta1/vmcluster_types.go b/api/operator/v1beta1/vmcluster_types.go index 3903ca6ec..7bbe83753 100644 --- a/api/operator/v1beta1/vmcluster_types.go +++ b/api/operator/v1beta1/vmcluster_types.go @@ -992,8 +992,7 @@ type VMAuthLoadBalancerSpec struct { // LogLevel for vmauth container. // +optional // +kubebuilder:validation:Enum=INFO;WARN;ERROR;FATAL;PANIC - LogLevel string `json:"logLevel,omitempty"` - CommonAppsParams `json:",inline"` + LogLevel string `json:"logLevel,omitempty"` // PodDisruptionBudget created by operator // +optional PodDisruptionBudget *EmbeddedPodDisruptionBudgetSpec `json:"podDisruptionBudget,omitempty"` @@ -1009,8 +1008,8 @@ type VMAuthLoadBalancerSpec struct { RollingUpdate *appsv1.RollingUpdateDeployment `json:"rollingUpdate,omitempty"` // License configures enterprise features license key // +optional - License *License `json:"license,omitempty"` - CommonConfigReloaderParams `json:",inline,omitempty"` + License *License `json:"license,omitempty"` + CommonAppsParams `json:",inline"` } // ProbePort returns port for probe requests diff --git a/api/operator/v1beta1/zz_generated.deepcopy.go b/api/operator/v1beta1/zz_generated.deepcopy.go index 0470edbbf..06aef6da8 100644 --- a/api/operator/v1beta1/zz_generated.deepcopy.go +++ b/api/operator/v1beta1/zz_generated.deepcopy.go @@ -4362,7 +4362,7 @@ func (in *VMAgentSpec) DeepCopyInto(out *VMAgentSpec) { } if in.ShardCount != nil { in, out := &in.ShardCount, &out.ShardCount - *out = new(int) + *out = new(int32) **out = **in } if in.UpdateStrategy != nil { @@ -4385,6 +4385,11 @@ func (in *VMAgentSpec) DeepCopyInto(out *VMAgentSpec) { *out = new(StorageSpec) (*in).DeepCopyInto(*out) } + if in.StatefulRollingUpdateStrategyBehavior != nil { + in, out := &in.StatefulRollingUpdateStrategyBehavior, &out.StatefulRollingUpdateStrategyBehavior + *out = new(StatefulSetUpdateStrategyBehavior) + (*in).DeepCopyInto(*out) + } if in.PersistentVolumeClaimRetentionPolicy != nil { in, out := &in.PersistentVolumeClaimRetentionPolicy, &out.PersistentVolumeClaimRetentionPolicy *out = new(appsv1.StatefulSetPersistentVolumeClaimRetentionPolicy) @@ -5214,7 +5219,6 @@ func (in *VMAuthLoadBalancerSpec) DeepCopyInto(out *VMAuthLoadBalancerSpec) { *out = new(VMServiceScrapeSpec) (*in).DeepCopyInto(*out) } - in.CommonAppsParams.DeepCopyInto(&out.CommonAppsParams) if in.PodDisruptionBudget != nil { in, out := &in.PodDisruptionBudget, &out.PodDisruptionBudget *out = new(EmbeddedPodDisruptionBudgetSpec) @@ -5235,7 +5239,7 @@ func (in *VMAuthLoadBalancerSpec) DeepCopyInto(out *VMAuthLoadBalancerSpec) { *out = new(License) (*in).DeepCopyInto(*out) } - in.CommonConfigReloaderParams.DeepCopyInto(&out.CommonConfigReloaderParams) + in.CommonAppsParams.DeepCopyInto(&out.CommonAppsParams) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VMAuthLoadBalancerSpec. diff --git a/config/crd/overlay/crd.descriptionless.yaml b/config/crd/overlay/crd.descriptionless.yaml index ca94e944b..bdf7e8d0d 100644 --- a/config/crd/overlay/crd.descriptionless.yaml +++ b/config/crd/overlay/crd.descriptionless.yaml @@ -6079,6 +6079,7 @@ spec: - spec type: object shardCount: + format: int32 type: integer startupProbe: type: object @@ -6087,6 +6088,14 @@ spec: type: boolean statefulRollingUpdateStrategy: type: string + statefulRollingUpdateStrategyBehavior: + properties: + maxUnavailable: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + type: object statefulStorage: properties: emptyDir: @@ -12326,6 +12335,7 @@ spec: type: object x-kubernetes-preserve-unknown-fields: true shardCount: + format: int32 type: integer startupProbe: type: object @@ -24730,6 +24740,14 @@ spec: type: boolean statefulRollingUpdateStrategy: type: string + statefulRollingUpdateStrategyBehavior: + properties: + maxUnavailable: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + type: object statefulStorage: properties: emptyDir: @@ -28631,6 +28649,14 @@ spec: type: boolean statefulRollingUpdateStrategy: type: string + statefulRollingUpdateStrategyBehavior: + properties: + maxUnavailable: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + type: object statefulStorage: properties: emptyDir: diff --git a/config/crd/overlay/crd.yaml b/config/crd/overlay/crd.yaml index 806f99e63..6af1e81e7 100644 --- a/config/crd/overlay/crd.yaml +++ b/config/crd/overlay/crd.yaml @@ -12041,6 +12041,7 @@ spec: in this case operator will use 1 deployment/sts per shard with replicas count according to spec.replicas, see [here](https://docs.victoriametrics.com/victoriametrics/vmagent/#scraping-big-number-of-targets) + format: int32 type: integer startupProbe: description: StartupProbe that will be added to CR pod @@ -12056,6 +12057,21 @@ spec: StatefulRollingUpdateStrategy allows configuration for strategyType set it to RollingUpdate for disabling operator statefulSet rollingUpdate type: string + statefulRollingUpdateStrategyBehavior: + description: |- + StatefulRollingUpdateStrategyBehavior defines customized behavior for rolling updates. + It applies if the RollingUpdateStrategy is set to OnDelete, which is the default. + properties: + maxUnavailable: + anyOf: + - type: integer + - type: string + description: |- + MaxUnavailable defines the maximum number of pods that can be unavailable during the update. + It can be specified as an absolute number (e.g. 2) or a percentage of the total pods (e.g. "50%"). + For example, if set to 100%, all pods will be upgraded at once, minimizing downtime when needed. + x-kubernetes-int-or-string: true + type: object statefulStorage: description: StatefulStorage configures storage for StatefulSet properties: @@ -24138,6 +24154,7 @@ spec: ShardCount - numbers of shards of VMAnomaly in this case operator will use 1 sts per shard with replicas count according to spec.replicas. + format: int32 type: integer startupProbe: description: StartupProbe that will be added to CR pod @@ -50092,6 +50109,21 @@ spec: StatefulRollingUpdateStrategy allows configuration for strategyType set it to RollingUpdate for disabling operator statefulSet rollingUpdate type: string + statefulRollingUpdateStrategyBehavior: + description: |- + StatefulRollingUpdateStrategyBehavior defines customized behavior for rolling updates. + It applies if the RollingUpdateStrategy is set to OnDelete, which is the default. + properties: + maxUnavailable: + anyOf: + - type: integer + - type: string + description: |- + MaxUnavailable defines the maximum number of pods that can be unavailable during the update. + It can be specified as an absolute number (e.g. 2) or a percentage of the total pods (e.g. "50%"). + For example, if set to 100%, all pods will be upgraded at once, minimizing downtime when needed. + x-kubernetes-int-or-string: true + type: object statefulStorage: description: StatefulStorage configures storage for StatefulSet properties: @@ -58885,6 +58917,21 @@ spec: StatefulRollingUpdateStrategy allows configuration for strategyType set it to RollingUpdate for disabling operator statefulSet rollingUpdate type: string + statefulRollingUpdateStrategyBehavior: + description: |- + StatefulRollingUpdateStrategyBehavior defines customized behavior for rolling updates. + It applies if the RollingUpdateStrategy is set to OnDelete, which is the default. + properties: + maxUnavailable: + anyOf: + - type: integer + - type: string + description: |- + MaxUnavailable defines the maximum number of pods that can be unavailable during the update. + It can be specified as an absolute number (e.g. 2) or a percentage of the total pods (e.g. "50%"). + For example, if set to 100%, all pods will be upgraded at once, minimizing downtime when needed. + x-kubernetes-int-or-string: true + type: object statefulStorage: description: StatefulStorage configures storage for StatefulSet diff --git a/config/manifests/bases/victoriametrics-operator.clusterserviceversion.yaml b/config/manifests/bases/victoriametrics-operator.clusterserviceversion.yaml index 4900a4857..3dbf2bee9 100644 --- a/config/manifests/bases/victoriametrics-operator.clusterserviceversion.yaml +++ b/config/manifests/bases/victoriametrics-operator.clusterserviceversion.yaml @@ -307,13 +307,6 @@ spec: kind: VMCluster name: vmclusters.operator.victoriametrics.com specDescriptors: - - description: |- - ConfigReloaderResources config-reloader container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ - if not defined default resources from operator config will be used - displayName: Resources - path: requestsLoadBalancer.spec.configReloaderResources - x-descriptors: - - urn:alm:descriptor:com.tectonic.ui:resourceRequirements - description: ReplicaCount is the expected size of the Application. displayName: Number of pods path: requestsLoadBalancer.spec.replicaCount diff --git a/docs/api.md b/docs/api.md index ee16f47d1..967d79047 100644 --- a/docs/api.md +++ b/docs/api.md @@ -1198,6 +1198,7 @@ Appears in: [VMDistributedZoneAgent](#vmdistributedzoneagent) | startupProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
StartupProbe that will be added to CR pod | | statefulMode#
_boolean_ | _(Optional)_
StatefulMode enables StatefulSet for `VMAgent` instead of Deployment
it allows using persistent storage for vmagent's persistentQueue | | statefulRollingUpdateStrategy#
_[StatefulSetUpdateStrategyType](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#statefulsetupdatestrategytype-v1-apps)_ | _(Optional)_
StatefulRollingUpdateStrategy allows configuration for strategyType
set it to RollingUpdate for disabling operator statefulSet rollingUpdate | +| statefulRollingUpdateStrategyBehavior#
_[StatefulSetUpdateStrategyBehavior](#statefulsetupdatestrategybehavior)_ | _(Optional)_
StatefulRollingUpdateStrategyBehavior defines customized behavior for rolling updates.
It applies if the RollingUpdateStrategy is set to OnDelete, which is the default. | | statefulStorage#
_[StorageSpec](#storagespec)_ | _(Optional)_
StatefulStorage configures storage for StatefulSet | | terminationGracePeriodSeconds#
_integer_ | _(Optional)_
TerminationGracePeriodSeconds period for container graceful termination | | tolerations#
_[Toleration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#toleration-v1-core) array_ | _(Optional)_
Tolerations If specified, the pod's tolerations. | @@ -1487,7 +1488,7 @@ Appears in: [VLAgentSpec](#vlagentspec), [VLInsert](#vlinsert), [VLSelect](#vlse #### CommonConfigReloaderParams -Appears in: [VMAgentSpec](#vmagentspec), [VMAlertSpec](#vmalertspec), [VMAlertmanagerSpec](#vmalertmanagerspec), [VMAuthLoadBalancerSpec](#vmauthloadbalancerspec), [VMAuthSpec](#vmauthspec) +Appears in: [VMAgentSpec](#vmagentspec), [VMAlertSpec](#vmalertspec), [VMAlertmanagerSpec](#vmalertmanagerspec), [VMAuthSpec](#vmauthspec) | Field | Description | | --- | --- | @@ -2762,7 +2763,7 @@ Appears in: [Receiver](#receiver) StatefulSetUpdateStrategyBehavior customizes behavior for StatefulSet updates. -Appears in: [VLStorage](#vlstorage), [VMSelect](#vmselect), [VMStorage](#vmstorage), [VTStorage](#vtstorage) +Appears in: [VLStorage](#vlstorage), [VMAgentSpec](#vmagentspec), [VMDistributedZoneAgentSpec](#vmdistributedzoneagentspec), [VMSelect](#vmselect), [VMStorage](#vmstorage), [VTStorage](#vtstorage) | Field | Description | | --- | --- | @@ -3292,6 +3293,7 @@ Appears in: [VMAgent](#vmagent) | startupProbe#
_[Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#probe-v1-core)_ | _(Optional)_
StartupProbe that will be added to CR pod | | statefulMode#
_boolean_ | _(Optional)_
StatefulMode enables StatefulSet for `VMAgent` instead of Deployment
it allows using persistent storage for vmagent's persistentQueue | | statefulRollingUpdateStrategy#
_[StatefulSetUpdateStrategyType](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#statefulsetupdatestrategytype-v1-apps)_ | _(Optional)_
StatefulRollingUpdateStrategy allows configuration for strategyType
set it to RollingUpdate for disabling operator statefulSet rollingUpdate | +| statefulRollingUpdateStrategyBehavior#
_[StatefulSetUpdateStrategyBehavior](#statefulsetupdatestrategybehavior)_ | _(Optional)_
StatefulRollingUpdateStrategyBehavior defines customized behavior for rolling updates.
It applies if the RollingUpdateStrategy is set to OnDelete, which is the default. | | statefulStorage#
_[StorageSpec](#storagespec)_ | _(Optional)_
StatefulStorage configures storage for StatefulSet | | staticScrapeNamespaceSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#labelselector-v1-meta)_ | _(Optional)_
StaticScrapeNamespaceSelector defines Namespaces to be selected for VMStaticScrape discovery.
Works in combination with NamespaceSelector.
NamespaceSelector nil - only objects at VMAgent namespace.
Selector nil - only objects at NamespaceSelector namespaces.
If both nil - behaviour controlled by selectAllByDefault | | staticScrapeRelabelTemplate#
_[RelabelConfig](#relabelconfig) array_ | _(Optional)_
StaticScrapeRelabelTemplate defines relabel config, that will be added to each VMStaticScrape.
it's useful for adding specific labels to all targets | @@ -3670,11 +3672,6 @@ Appears in: [VMAuthLoadBalancer](#vmauthloadbalancer) | --- | --- | | affinity#
_[Affinity](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#affinity-v1-core)_ | _(Optional)_
Affinity If specified, the pod's scheduling constraints. | | configMaps#
_string array_ | _(Optional)_
ConfigMaps is a list of ConfigMaps in the same namespace as the Application
object, which shall be mounted into the Application container
at /etc/vm/configs/CONFIGMAP_NAME folder | -| configReloadAuthKeySecret#
_[SecretKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#secretkeyselector-v1-core)_ | _(Optional)_
ConfigReloadAuthKeySecret defines optional secret reference authKey for /-/reload API requests.
Given secret reference will be added to the application and vm-config-reloader as volume
available since v0.57.0 version | -| configReloaderExtraArgs#
_object (keys:string, values:string)_ | _(Optional)_
ConfigReloaderExtraArgs that will be passed to VMAuths config-reloader container
for example resync-interval: "30s" | -| configReloaderImage#
_string_ | _(Optional)_
ConfigReloaderImage defines image:tag for config-reloader container | -| configReloaderImageTag#
_string_ | _(Optional)_
ConfigReloaderImageTag defines image:tag for config-reloader container
Deprecated: since version v0.67.0 will be removed in v0.69.0 use configReloaderImage instead
| -| configReloaderResources#
_[ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#resourcerequirements-v1-core)_ | _(Optional)_
ConfigReloaderResources config-reloader container resource request and limits, https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
if not defined default resources from operator config will be used | | containers#
_[Container](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#container-v1-core) array_ | _(Optional)_
Containers property allows to inject additions sidecars or to patch existing containers.
It can be useful for proxies, backup, etc. | | disableAutomountServiceAccountToken#
_boolean_ | _(Optional)_
DisableAutomountServiceAccountToken whether to disable serviceAccount auto mount by Kubernetes (available from v0.54.0).
Operator will conditionally create volumes and volumeMounts for containers if it requires k8s API access.
For example, vmagent and vm-config-reloader requires k8s API access.
Operator creates volumes with name: "kube-api-access", which can be used as volumeMount for extraContainers if needed.
And also adds VolumeMounts at /var/run/secrets/kubernetes.io/serviceaccount. | | disableSelfServiceScrape#
_boolean_ | _(Optional)_
DisableSelfServiceScrape controls creation of VMServiceScrape by operator
for the application.
Has priority over `VM_DISABLESELFSERVICESCRAPECREATION` operator env variable | @@ -3719,7 +3716,6 @@ Appears in: [VMAuthLoadBalancer](#vmauthloadbalancer) | updateStrategy#
_[DeploymentStrategyType](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#deploymentstrategytype-v1-apps)_ | _(Optional)_
UpdateStrategy - overrides default update strategy.
Available from operator v0.64.0 | | useDefaultResources#
_boolean_ | _(Optional)_
UseDefaultResources controls resource settings
By default, operator sets built-in resource requirements | | useStrictSecurity#
_boolean_ | _(Optional)_
UseStrictSecurity enables strict security mode for component
it restricts disk writes access
uses non-root user out of the box
drops not needed security permissions | -| useVMConfigReloader#
_boolean_ | _(Optional)_
UseVMConfigReloader replaces prometheus-like config-reloader
with vm one. It uses secrets watch instead of file watch
which greatly increases speed of config updates
Removed since v0.67.0: this property is ignored and no longer needed | | volumeMounts#
_[VolumeMount](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#volumemount-v1-core) array_ | _(Optional)_
VolumeMounts allows configuration of additional VolumeMounts on the output Deployment/StatefulSet definition.
VolumeMounts specified will be appended to other VolumeMounts in the Application container | | volumes#
_[Volume](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#volume-v1-core) array_ | _(Required)_
Volumes allows configuration of additional volumes on the output Deployment/StatefulSet definition.
Volumes specified will be appended to other volumes that are generated.
/ +optional | diff --git a/internal/controller/operator/factory/build/shard.go b/internal/controller/operator/factory/build/shard.go index 864869b15..f1616b084 100644 --- a/internal/controller/operator/factory/build/shard.go +++ b/internal/controller/operator/factory/build/shard.go @@ -3,7 +3,6 @@ package build import ( "fmt" "iter" - "strconv" policyv1 "k8s.io/api/policy/v1" @@ -23,9 +22,9 @@ type shardOpts interface { } // ShardNumIter iterates over shardCount in order defined in backward -func ShardNumIter(backward bool, shardCount int) iter.Seq[int] { +func ShardNumIter(backward bool, shardCount int32) iter.Seq[int32] { if backward { - return func(yield func(int) bool) { + return func(yield func(int32) bool) { for shardCount > 0 { shardCount-- num := shardCount @@ -35,8 +34,8 @@ func ShardNumIter(backward bool, shardCount int) iter.Seq[int] { } } } - return func(yield func(int) bool) { - for i := 0; i < shardCount; i++ { + return func(yield func(int32) bool) { + for i := int32(0); i < shardCount; i++ { if !yield(i) { return } @@ -72,19 +71,19 @@ func ShardPodLabels(cr shardOpts) map[string]string { } // RenderShard replaces resource's shard number placeholder with a given shard number -func RenderShard[T any](resource *T, num int) (*T, error) { +func RenderShard[T any](resource *T, num int32) (*T, error) { placeholders := map[string]string{ - shardNumPlaceholder: strconv.Itoa(num), + shardNumPlaceholder: fmt.Sprintf("%d", num), } return k8stools.RenderPlaceholders(resource, placeholders) } // ShardPodDisruptionBudget creates object for given CRD and shard num -func ShardPodDisruptionBudget(cr shardOpts, spec *vmv1beta1.EmbeddedPodDisruptionBudgetSpec, num int) *policyv1.PodDisruptionBudget { +func ShardPodDisruptionBudget(cr shardOpts, spec *vmv1beta1.EmbeddedPodDisruptionBudgetSpec, num int32) *policyv1.PodDisruptionBudget { pdb := PodDisruptionBudget(cr, spec) if cr.IsSharded() { pdb.Name = fmt.Sprintf("%s-%d", pdb.Name, num) - pdb.Spec.Selector.MatchLabels[shardLabelName] = strconv.Itoa(num) + pdb.Spec.Selector.MatchLabels[shardLabelName] = fmt.Sprintf("%d", num) } return pdb } diff --git a/internal/controller/operator/factory/build/shard_test.go b/internal/controller/operator/factory/build/shard_test.go index ace5c474e..a22d015f1 100644 --- a/internal/controller/operator/factory/build/shard_test.go +++ b/internal/controller/operator/factory/build/shard_test.go @@ -8,10 +8,10 @@ import ( ) func TestShardNumIter(t *testing.T) { - f := func(backward bool, upperBound int) { + f := func(backward bool, upperBound int32) { output := slices.Collect(ShardNumIter(backward, upperBound)) - assert.Equal(t, upperBound, len(output), "invalid ShardNumIter() items count") - var lowerBound int + assert.Len(t, output, int(upperBound), "invalid ShardNumIter() items count") + var lowerBound int32 if backward { lowerBound = upperBound - 1 upperBound = 0 diff --git a/internal/controller/operator/factory/reconcile/deploy.go b/internal/controller/operator/factory/reconcile/deploy.go index 5a2cd681a..e42276ce9 100644 --- a/internal/controller/operator/factory/reconcile/deploy.go +++ b/internal/controller/operator/factory/reconcile/deploy.go @@ -18,17 +18,25 @@ import ( "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/logger" ) +type DeploymentOpts struct { + PatchSpec func(existingSpec, newSpec *appsv1.DeploymentSpec) +} + // Deployment performs an update or create operator for deployment and waits until it's replicas is ready -func Deployment(ctx context.Context, rclient client.Client, newObj, prevObj *appsv1.Deployment, hasHPA bool, owner *metav1.OwnerReference) error { +func Deployment(ctx context.Context, rclient client.Client, newObj, prevObj *appsv1.Deployment, owner *metav1.OwnerReference, o *DeploymentOpts) error { var prevMeta *metav1.ObjectMeta var prevTemplateAnnotations map[string]string if prevObj != nil { prevMeta = &prevObj.ObjectMeta prevTemplateAnnotations = prevObj.Spec.Template.Annotations } + rclient.Scheme().Default(newObj) nsn := types.NamespacedName{Name: newObj.Name, Namespace: newObj.Namespace} removeFinalizer := true + if o == nil { + o = new(DeploymentOpts) + } err := retryOnConflict(func() error { var existingObj appsv1.Deployment if err := rclient.Get(ctx, nsn, &existingObj); err != nil { @@ -44,16 +52,15 @@ func Deployment(ctx context.Context, rclient client.Client, newObj, prevObj *app if err := collectGarbage(ctx, rclient, &existingObj, removeFinalizer); err != nil { return err } - spec := &newObj.Spec - if hasHPA { - spec.Replicas = existingObj.Spec.Replicas + if o.PatchSpec != nil { + o.PatchSpec(&existingObj.Spec, &newObj.Spec) } metaChanged, err := mergeMeta(&existingObj, newObj, prevMeta, owner, removeFinalizer) if err != nil { return err } logMessageMetadata := []string{fmt.Sprintf("name=%s, is_prev_nil=%t", nsn.String(), prevObj == nil)} - spec.Template.Annotations = mergeMaps(existingObj.Spec.Template.Annotations, newObj.Spec.Template.Annotations, prevTemplateAnnotations) + newObj.Spec.Template.Annotations = mergeMaps(existingObj.Spec.Template.Annotations, newObj.Spec.Template.Annotations, prevTemplateAnnotations) specDiff := diffDeepDerivative(newObj.Spec, existingObj.Spec, "spec") needsUpdate := metaChanged || len(specDiff) > 0 if !needsUpdate { diff --git a/internal/controller/operator/factory/reconcile/deploy_test.go b/internal/controller/operator/factory/reconcile/deploy_test.go index 019abd0f2..4fcd5551c 100644 --- a/internal/controller/operator/factory/reconcile/deploy_test.go +++ b/internal/controller/operator/factory/reconcile/deploy_test.go @@ -21,9 +21,9 @@ func TestDeployReconcile(t *testing.T) { new, prev *appsv1.Deployment predefinedObjects []runtime.Object actions []k8stools.ClientAction - hasHPA bool validate func(*appsv1.Deployment) wantErr bool + o *DeploymentOpts } getDeploy := func(fns ...func(d *appsv1.Deployment)) *appsv1.Deployment { d := &appsv1.Deployment{ @@ -76,7 +76,7 @@ func TestDeployReconcile(t *testing.T) { ctx := context.Background() cl := k8stools.GetTestClientWithActionsAndObjects(o.predefinedObjects) synctest.Test(t, func(t *testing.T) { - err := Deployment(ctx, cl, o.new, o.prev, o.hasHPA, nil) + err := Deployment(ctx, cl, o.new, o.prev, nil, o.o) if o.wantErr { assert.Error(t, err) } else { @@ -152,4 +152,34 @@ func TestDeployReconcile(t *testing.T) { {Verb: "Get", Kind: "Deployment", Resource: nn}, }, }) + + // do not update with custom patch + f(opts{ + new: getDeploy(func(d *appsv1.Deployment) { + d.Spec.Replicas = ptr.To[int32](1) + }), + prev: getDeploy(func(d *appsv1.Deployment) { + d.Spec.Template.Annotations = map[string]string{ + "new-annotation": "value", + } + }), + predefinedObjects: []runtime.Object{ + getDeploy(func(d *appsv1.Deployment) { + d.Spec.Replicas = ptr.To[int32](2) + d.Status.ReadyReplicas = 2 + d.Status.UpdatedReplicas = 2 + d.Status.Replicas = 2 + d.Status.Conditions[0].Reason = "ReplicaSetUpdated" + }), + }, + actions: []k8stools.ClientAction{ + {Verb: "Get", Kind: "Deployment", Resource: nn}, + {Verb: "Get", Kind: "Deployment", Resource: nn}, + }, + o: &DeploymentOpts{ + PatchSpec: func(existingSpec, newSpec *appsv1.DeploymentSpec) { + newSpec.Replicas = existingSpec.Replicas + }, + }, + }) } diff --git a/internal/controller/operator/factory/reconcile/statefulset.go b/internal/controller/operator/factory/reconcile/statefulset.go index 25560db24..ff61f4284 100644 --- a/internal/controller/operator/factory/reconcile/statefulset.go +++ b/internal/controller/operator/factory/reconcile/statefulset.go @@ -29,14 +29,12 @@ import ( const podRevisionLabel = "controller-revision-hash" -// STSOptions options for StatefulSet update +// StatefulSetOpts options for StatefulSet update // HPA and UpdateReplicaCount optional -type STSOptions struct { - HasClaim bool - SelectorLabels func() map[string]string - HPA *vmv1beta1.EmbeddedHPA - UpdateReplicaCount func(count *int32) - UpdateBehavior *vmv1beta1.StatefulSetUpdateStrategyBehavior +type StatefulSetOpts struct { + SelectorLabels map[string]string + PatchSpec func(existingSpec, newSpec *appsv1.StatefulSetSpec) + UpdateBehavior *vmv1beta1.StatefulSetUpdateStrategyBehavior } func waitForStatefulSetReady(ctx context.Context, rclient client.Client, newObj *appsv1.StatefulSet) error { @@ -69,7 +67,7 @@ func waitForStatefulSetReady(ctx context.Context, rclient client.Client, newObj } // StatefulSet performs create and update operations for given statefulSet with STSOptions -func StatefulSet(ctx context.Context, rclient client.Client, cr STSOptions, newObj, prevObj *appsv1.StatefulSet, owner *metav1.OwnerReference) error { +func StatefulSet(ctx context.Context, rclient client.Client, newObj, prevObj *appsv1.StatefulSet, owner *metav1.OwnerReference, o *StatefulSetOpts) error { if err := validateStatefulSet(newObj); err != nil { return err } @@ -87,6 +85,9 @@ func StatefulSet(ctx context.Context, rclient client.Client, cr STSOptions, newO updateStrategy := newObj.Spec.UpdateStrategy.Type nsn := types.NamespacedName{Name: newObj.Name, Namespace: newObj.Namespace} removeFinalizer := true + if o == nil { + o = new(StatefulSetOpts) + } err := retryOnConflict(func() error { var existingObj appsv1.StatefulSet if err := rclient.Get(ctx, nsn, &existingObj); err != nil { @@ -104,16 +105,8 @@ func StatefulSet(ctx context.Context, rclient client.Client, cr STSOptions, newO return err } - spec := &newObj.Spec - // will update the original cr replicaCount to propagate right num, - // for now, it's only used in vmselect - if cr.UpdateReplicaCount != nil { - cr.UpdateReplicaCount(existingObj.Spec.Replicas) - } - - // do not change replicas count. - if cr.HPA != nil { - spec.Replicas = existingObj.Spec.Replicas + if o.PatchSpec != nil { + o.PatchSpec(&existingObj.Spec, &newObj.Spec) } mustRecreateSTS, mustRecreatePod := isSTSRecreateRequired(ctx, &existingObj, newObj, prevVCTs) @@ -132,7 +125,7 @@ func StatefulSet(ctx context.Context, rclient client.Client, cr STSOptions, newO } // check if pvcs need to resize - if cr.HasClaim { + if len(newObj.Spec.VolumeClaimTemplates) > 0 { if err := updateSTSPVC(ctx, rclient, &existingObj, prevVCTs); err != nil { return err } @@ -143,7 +136,7 @@ func StatefulSet(ctx context.Context, rclient client.Client, cr STSOptions, newO return err } logMessageMetadata := []string{fmt.Sprintf("name=%s, is_prev_nil=%t", nsn.String(), prevObj == nil)} - spec.Template.Annotations = mergeMaps(existingObj.Spec.Template.Annotations, newObj.Spec.Template.Annotations, prevTemplateAnnotations) + newObj.Spec.Template.Annotations = mergeMaps(existingObj.Spec.Template.Annotations, newObj.Spec.Template.Annotations, prevTemplateAnnotations) specDiff := diffDeepDerivative(newObj.Spec, existingObj.Spec, "spec") needsUpdate := metaChanged || len(specDiff) > 0 if !needsUpdate { @@ -165,14 +158,14 @@ func StatefulSet(ctx context.Context, rclient client.Client, cr STSOptions, newO case appsv1.OnDeleteStatefulSetStrategyType: opts := rollingUpdateOpts{ recreate: recreatePod, - selector: cr.SelectorLabels(), + selector: o.SelectorLabels, maxUnavailable: 1, } - if cr.UpdateBehavior != nil { - if cr.UpdateBehavior.MaxUnavailable.String() == "100%" { + if o.UpdateBehavior != nil { + if o.UpdateBehavior.MaxUnavailable.String() == "100%" { opts.delete = true } - opts.maxUnavailable, err = intstr.GetScaledValueFromIntOrPercent(cr.UpdateBehavior.MaxUnavailable, int(*newObj.Spec.Replicas), false) + opts.maxUnavailable, err = intstr.GetScaledValueFromIntOrPercent(o.UpdateBehavior.MaxUnavailable, int(*newObj.Spec.Replicas), false) if err != nil { return err } diff --git a/internal/controller/operator/factory/reconcile/statefulset_pvc_expand.go b/internal/controller/operator/factory/reconcile/statefulset_pvc_expand.go index 08b669ea7..d32781a00 100644 --- a/internal/controller/operator/factory/reconcile/statefulset_pvc_expand.go +++ b/internal/controller/operator/factory/reconcile/statefulset_pvc_expand.go @@ -121,8 +121,6 @@ func updateSTSPVC(ctx context.Context, rclient client.Client, sts *appsv1.Statef if err := updatePVC(ctx, rclient, &pvc, &stsClaim, prevVCT, nil); err != nil { return err } - } - for _, pvc := range pvcs.Items { nsnPvc := types.NamespacedName{Name: pvc.Name, Namespace: pvc.Namespace} size := pvc.Spec.Resources.Requests[corev1.ResourceStorage] if err := waitForPVCReady(ctx, rclient, nsnPvc, size); err != nil { diff --git a/internal/controller/operator/factory/reconcile/statefulset_pvc_expand_test.go b/internal/controller/operator/factory/reconcile/statefulset_pvc_expand_test.go index 1a36fea4d..9198bb225 100644 --- a/internal/controller/operator/factory/reconcile/statefulset_pvc_expand_test.go +++ b/internal/controller/operator/factory/reconcile/statefulset_pvc_expand_test.go @@ -506,8 +506,8 @@ func Test_updateSTSPVC(t *testing.T) { }, actions: []k8stools.ClientAction{ {Verb: "Update", Kind: "PersistentVolumeClaim", Resource: pvc2NSN}, - {Verb: "Update", Kind: "PersistentVolumeClaim", Resource: pvc1NSN}, {Verb: "Get", Kind: "PersistentVolumeClaim", Resource: pvc2NSN}, + {Verb: "Update", Kind: "PersistentVolumeClaim", Resource: pvc1NSN}, {Verb: "Get", Kind: "PersistentVolumeClaim", Resource: pvc1NSN}, }, expected: []corev1.PersistentVolumeClaim{ diff --git a/internal/controller/operator/factory/reconcile/statefulset_test.go b/internal/controller/operator/factory/reconcile/statefulset_test.go index dfd3b585e..4a4ae9c55 100644 --- a/internal/controller/operator/factory/reconcile/statefulset_test.go +++ b/internal/controller/operator/factory/reconcile/statefulset_test.go @@ -832,6 +832,7 @@ func TestStatefulsetReconcile(t *testing.T) { validate func(*appsv1.StatefulSet) actions []k8stools.ClientAction wantErr bool + o *StatefulSetOpts } getSts := func(fns ...func(s *appsv1.StatefulSet)) *appsv1.StatefulSet { s := &appsv1.StatefulSet{ @@ -879,9 +880,8 @@ func TestStatefulsetReconcile(t *testing.T) { t.Helper() ctx := context.Background() cl := k8stools.GetTestClientWithActionsAndObjects(o.predefinedObjects) - var emptyOpts STSOptions synctest.Test(t, func(t *testing.T) { - err := StatefulSet(ctx, cl, emptyOpts, o.new, o.prev, nil) + err := StatefulSet(ctx, cl, o.new, o.prev, nil, o.o) if o.wantErr { assert.Error(t, err) return @@ -1070,6 +1070,120 @@ func TestStatefulsetReconcile(t *testing.T) { }, wantErr: true, }) + + // do not update with custom patch + f(opts{ + new: getSts(), + prev: getSts(), + predefinedObjects: []runtime.Object{ + getSts(func(s *appsv1.StatefulSet) { + s.Spec.Replicas = ptr.To[int32](6) + s.Status.Replicas = 6 + s.Status.ReadyReplicas = 6 + s.Status.UpdatedReplicas = 6 + }), + &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-0", + Namespace: "default", + Labels: map[string]string{ + podRevisionLabel: "some-version", + "label": "value", + }, + }, + Status: corev1.PodStatus{ + Conditions: []corev1.PodCondition{ + {Status: "True", Type: corev1.PodReady}, + }, + Phase: corev1.PodRunning, + }, + }, + &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-1-1", + Namespace: "default", + Labels: map[string]string{ + "label": "value", + }, + }, + Status: corev1.PodStatus{ + Conditions: []corev1.PodCondition{ + {Status: "True", Type: corev1.PodReady}, + }, + Phase: corev1.PodRunning, + }, + }, + &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-1-2", + Namespace: "default", + Labels: map[string]string{ + "label": "value", + }, + }, + Status: corev1.PodStatus{ + Conditions: []corev1.PodCondition{ + {Status: "True", Type: corev1.PodReady}, + }, + Phase: corev1.PodRunning, + }, + }, + &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-1-3", + Namespace: "default", + Labels: map[string]string{ + "label": "value", + }, + }, + Status: corev1.PodStatus{ + Conditions: []corev1.PodCondition{ + {Status: "True", Type: corev1.PodReady}, + }, + Phase: corev1.PodRunning, + }, + }, + &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-1-4", + Namespace: "default", + Labels: map[string]string{ + "label": "value", + }, + }, + Status: corev1.PodStatus{ + Conditions: []corev1.PodCondition{ + {Status: "True", Type: corev1.PodReady}, + }, + Phase: corev1.PodRunning, + }, + }, + &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-1-5", + Namespace: "default", + Labels: map[string]string{ + "label": "value", + }, + }, + Status: corev1.PodStatus{ + Conditions: []corev1.PodCondition{ + {Status: "True", Type: corev1.PodReady}, + }, + Phase: corev1.PodRunning, + }, + }, + }, + actions: []k8stools.ClientAction{ + {Verb: "Get", Kind: "StatefulSet", Resource: nn}, + {Verb: "Get", Kind: "StatefulSet", Resource: nn}, + }, + o: &StatefulSetOpts{ + PatchSpec: func(existingSpec, newSpec *appsv1.StatefulSetSpec) { + newSpec.Replicas = existingSpec.Replicas + }, + }, + }) } func TestValidateStatefulSetFail(t *testing.T) { diff --git a/internal/controller/operator/factory/vlagent/vlagent.go b/internal/controller/operator/factory/vlagent/vlagent.go index 6654ef576..95207f5c5 100644 --- a/internal/controller/operator/factory/vlagent/vlagent.go +++ b/internal/controller/operator/factory/vlagent/vlagent.go @@ -161,11 +161,10 @@ func createOrUpdateDeploy(ctx context.Context, rclient client.Client, cr, prevCR if prevAppObj != nil { prevApp, _ = prevAppObj.(*appsv1.StatefulSet) } - stsOpts := reconcile.STSOptions{ - HasClaim: len(newApp.Spec.VolumeClaimTemplates) > 0, - SelectorLabels: cr.SelectorLabels, + o := reconcile.StatefulSetOpts{ + SelectorLabels: cr.SelectorLabels(), } - if err := reconcile.StatefulSet(ctx, rclient, stsOpts, newApp, prevApp, &owner); err != nil { + if err := reconcile.StatefulSet(ctx, rclient, newApp, prevApp, &owner, &o); err != nil { return fmt.Errorf("cannot reconcile statefulset for vlagent: %w", err) } return nil diff --git a/internal/controller/operator/factory/vlcluster/vlinsert.go b/internal/controller/operator/factory/vlcluster/vlinsert.go index 06931cfbb..7a9a04887 100644 --- a/internal/controller/operator/factory/vlcluster/vlinsert.go +++ b/internal/controller/operator/factory/vlcluster/vlinsert.go @@ -75,7 +75,14 @@ func createOrUpdateVLInsertDeployment(ctx context.Context, rclient client.Client return err } owner := cr.AsOwner() - return reconcile.Deployment(ctx, rclient, newDeployment, prevDeploy, cr.Spec.VLInsert.HPA != nil, &owner) + o := reconcile.DeploymentOpts{ + PatchSpec: func(existingSpec, newSpec *appsv1.DeploymentSpec) { + if cr.Spec.VLInsert.HPA != nil { + newSpec.Replicas = existingSpec.Replicas + } + }, + } + return reconcile.Deployment(ctx, rclient, newDeployment, prevDeploy, &owner, &o) } func buildVLInsertDeployment(cr *vmv1.VLCluster) (*appsv1.Deployment, error) { diff --git a/internal/controller/operator/factory/vlcluster/vlselect.go b/internal/controller/operator/factory/vlcluster/vlselect.go index 77cdf4e26..c3f86d023 100644 --- a/internal/controller/operator/factory/vlcluster/vlselect.go +++ b/internal/controller/operator/factory/vlcluster/vlselect.go @@ -182,7 +182,14 @@ func createOrUpdateVLSelectDeployment(ctx context.Context, rclient client.Client return err } owner := cr.AsOwner() - return reconcile.Deployment(ctx, rclient, newDep, prevDep, cr.Spec.VLSelect.HPA != nil, &owner) + o := reconcile.DeploymentOpts{ + PatchSpec: func(existingSpec, newSpec *appsv1.DeploymentSpec) { + if cr.Spec.VLSelect.HPA != nil { + newSpec.Replicas = existingSpec.Replicas + } + }, + } + return reconcile.Deployment(ctx, rclient, newDep, prevDep, &owner, &o) } func buildVLSelectDeployment(cr *vmv1.VLCluster) (*appsv1.Deployment, error) { diff --git a/internal/controller/operator/factory/vlcluster/vlstorage.go b/internal/controller/operator/factory/vlcluster/vlstorage.go index 48679d8ff..01742e7ca 100644 --- a/internal/controller/operator/factory/vlcluster/vlstorage.go +++ b/internal/controller/operator/factory/vlcluster/vlstorage.go @@ -166,15 +166,17 @@ func createOrUpdateVLStorageSTS(ctx context.Context, rclient client.Client, cr, return err } - stsOpts := reconcile.STSOptions{ - HasClaim: len(newSts.Spec.VolumeClaimTemplates) > 0, - SelectorLabels: func() map[string]string { - return cr.SelectorLabels(vmv1beta1.ClusterComponentStorage) - }, + o := reconcile.StatefulSetOpts{ + SelectorLabels: cr.SelectorLabels(vmv1beta1.ClusterComponentStorage), UpdateBehavior: cr.Spec.VLStorage.RollingUpdateStrategyBehavior, + PatchSpec: func(existingSpec, newSpec *appsv1.StatefulSetSpec) { + if cr.Spec.VLStorage.HPA != nil { + newSpec.Replicas = existingSpec.Replicas + } + }, } owner := cr.AsOwner() - return reconcile.StatefulSet(ctx, rclient, stsOpts, newSts, prevSts, &owner) + return reconcile.StatefulSet(ctx, rclient, newSts, prevSts, &owner, &o) } func buildVLStorageSTSSpec(cr *vmv1.VLCluster) (*appsv1.StatefulSet, error) { diff --git a/internal/controller/operator/factory/vlcluster/vmauth_lb.go b/internal/controller/operator/factory/vlcluster/vmauth_lb.go index f7fd19f71..117230a39 100644 --- a/internal/controller/operator/factory/vlcluster/vmauth_lb.go +++ b/internal/controller/operator/factory/vlcluster/vmauth_lb.go @@ -44,7 +44,7 @@ func createOrUpdateVMAuthLB(ctx context.Context, rclient client.Client, cr, prev return fmt.Errorf("cannot build prev deployment for vmauth loadbalancing: %w", err) } } - if err := reconcile.Deployment(ctx, rclient, lbDep, prevLB, false, &owner); err != nil { + if err := reconcile.Deployment(ctx, rclient, lbDep, prevLB, &owner, nil); err != nil { return fmt.Errorf("cannot reconcile vmauth lb deployment: %w", err) } if err := createOrUpdateVMAuthLBService(ctx, rclient, cr, prevCR); err != nil { diff --git a/internal/controller/operator/factory/vlsingle/vlsingle.go b/internal/controller/operator/factory/vlsingle/vlsingle.go index 89786210a..387ed92c1 100644 --- a/internal/controller/operator/factory/vlsingle/vlsingle.go +++ b/internal/controller/operator/factory/vlsingle/vlsingle.go @@ -108,7 +108,7 @@ func CreateOrUpdate(ctx context.Context, rclient client.Client, cr *vmv1.VLSingl return fmt.Errorf("cannot generate new deploy for vlsingle: %w", err) } - return reconcile.Deployment(ctx, rclient, newDeploy, prevDeploy, false, &owner) + return reconcile.Deployment(ctx, rclient, newDeploy, prevDeploy, &owner, nil) } func newDeployment(r *vmv1.VLSingle) (*appsv1.Deployment, error) { diff --git a/internal/controller/operator/factory/vmagent/vmagent.go b/internal/controller/operator/factory/vmagent/vmagent.go index 0ac851a47..9d51bc4ee 100644 --- a/internal/controller/operator/factory/vmagent/vmagent.go +++ b/internal/controller/operator/factory/vmagent/vmagent.go @@ -203,7 +203,7 @@ func createOrUpdateApp(ctx context.Context, rclient client.Client, cr, prevCR *v } rtCh := make(chan *returnValue) owner := cr.AsOwner() - updateShard := func(shardNum int) { + updateShard := func(shardNum int32) { var rv returnValue defer func() { rtCh <- &rv @@ -255,8 +255,7 @@ func createOrUpdateApp(ctx context.Context, rclient client.Client, cr, prevCR *v } } } - - if err := reconcile.Deployment(ctx, rclient, newApp, prevApp, false, &owner); err != nil { + if err := reconcile.Deployment(ctx, rclient, newApp, prevApp, &owner, nil); err != nil { rv.err = fmt.Errorf("cannot reconcile deployment for vmagent(%d): %w", shardNum, err) return } @@ -294,13 +293,11 @@ func createOrUpdateApp(ctx context.Context, rclient client.Client, cr, prevCR *v } } selectorLabels := maps.Clone(newApp.Spec.Selector.MatchLabels) - opts := reconcile.STSOptions{ - HasClaim: len(newApp.Spec.VolumeClaimTemplates) > 0, - SelectorLabels: func() map[string]string { - return selectorLabels - }, + o := reconcile.StatefulSetOpts{ + SelectorLabels: selectorLabels, + UpdateBehavior: cr.Spec.StatefulRollingUpdateStrategyBehavior, } - if err := reconcile.StatefulSet(ctx, rclient, opts, newApp, prevApp, &owner); err != nil { + if err := reconcile.StatefulSet(ctx, rclient, newApp, prevApp, &owner, &o); err != nil { rv.err = fmt.Errorf("cannot reconcile %T for vmagent(%d): %w", newApp, shardNum, err) return } @@ -726,7 +723,7 @@ func newPodSpec(cr *vmv1beta1.VMAgent, ac *build.AssetsCache) (*corev1.PodSpec, }, nil } -func patchShardContainers(containers []corev1.Container, shardNum, shardCount int) { +func patchShardContainers(containers []corev1.Container, shardNum, shardCount int32) { for i := range containers { container := &containers[i] if container.Name == "vmagent" { diff --git a/internal/controller/operator/factory/vmagent/vmagent_test.go b/internal/controller/operator/factory/vmagent/vmagent_test.go index 94311fec1..6a3ed6a09 100644 --- a/internal/controller/operator/factory/vmagent/vmagent_test.go +++ b/internal/controller/operator/factory/vmagent/vmagent_test.go @@ -134,7 +134,7 @@ func TestCreateOrUpdate(t *testing.T) { RemoteWrite: []vmv1beta1.VMAgentRemoteWriteSpec{ {URL: "http://remote-write"}, }, - ShardCount: func() *int { i := 2; return &i }(), + ShardCount: func() *int32 { i := int32(2); return &i }(), }, }, predefinedObjects: []runtime.Object{ @@ -455,7 +455,7 @@ func TestCreateOrUpdate(t *testing.T) { CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To[int32](2), }, - ShardCount: ptr.To(3), + ShardCount: ptr.To[int32](3), PodDisruptionBudget: &vmv1beta1.EmbeddedPodDisruptionBudgetSpec{ MinAvailable: ptr.To(intstr.FromInt(1)), }, diff --git a/internal/controller/operator/factory/vmalert/vmalert.go b/internal/controller/operator/factory/vmalert/vmalert.go index b056bee79..9ee4df37d 100644 --- a/internal/controller/operator/factory/vmalert/vmalert.go +++ b/internal/controller/operator/factory/vmalert/vmalert.go @@ -154,7 +154,7 @@ func CreateOrUpdate(ctx context.Context, cr *vmv1beta1.VMAlert, rclient client.C return err } - return reconcile.Deployment(ctx, rclient, newDeploy, prevDeploy, false, &owner) + return reconcile.Deployment(ctx, rclient, newDeploy, prevDeploy, &owner, nil) } // newDeploy returns a busybox pod with the same name/namespace as the cr diff --git a/internal/controller/operator/factory/vmalertmanager/alertmanager.go b/internal/controller/operator/factory/vmalertmanager/alertmanager.go index 27d66d844..3529f7818 100644 --- a/internal/controller/operator/factory/vmalertmanager/alertmanager.go +++ b/internal/controller/operator/factory/vmalertmanager/alertmanager.go @@ -70,11 +70,10 @@ func CreateOrUpdateAlertManager(ctx context.Context, cr *vmv1beta1.VMAlertmanage return fmt.Errorf("cannot generate alertmanager sts, name: %s,err: %w", cr.Name, err) } - stsOpts := reconcile.STSOptions{ - HasClaim: len(newSts.Spec.VolumeClaimTemplates) > 0, - SelectorLabels: cr.SelectorLabels, + o := reconcile.StatefulSetOpts{ + SelectorLabels: cr.SelectorLabels(), } - return reconcile.StatefulSet(ctx, rclient, stsOpts, newSts, prevSts, &owner) + return reconcile.StatefulSet(ctx, rclient, newSts, prevSts, &owner, &o) } func deleteOrphaned(ctx context.Context, rclient client.Client, cr *vmv1beta1.VMAlertmanager) error { diff --git a/internal/controller/operator/factory/vmanomaly/statefulset.go b/internal/controller/operator/factory/vmanomaly/statefulset.go index bda3c19ec..63f6acac1 100644 --- a/internal/controller/operator/factory/vmanomaly/statefulset.go +++ b/internal/controller/operator/factory/vmanomaly/statefulset.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "maps" - "strconv" "sync" appsv1 "k8s.io/api/apps/v1" @@ -89,7 +88,7 @@ func CreateOrUpdate(ctx context.Context, cr *vmv1.VMAnomaly, rclient client.Clie return createOrUpdateApp(ctx, rclient, cr, prevCR, newAppTpl, prevAppTpl) } -func patchShardContainers(containers []corev1.Container, shardNum, shardCount int) { +func patchShardContainers(containers []corev1.Container, shardNum, shardCount int32) { for i := range containers { container := &containers[i] if container.Name != "vmanomaly" { @@ -105,11 +104,11 @@ func patchShardContainers(containers []corev1.Container, shardNum, shardCount in envs = append(envs, []corev1.EnvVar{ { Name: "VMANOMALY_MEMBERS_COUNT", - Value: strconv.Itoa(shardCount), + Value: fmt.Sprintf("%d", shardCount), }, { Name: "VMANOMALY_MEMBER_NUM", - Value: strconv.Itoa(shardNum), + Value: fmt.Sprintf("%d", shardNum), }, }...) container.Env = envs @@ -205,7 +204,7 @@ func createOrUpdateApp(ctx context.Context, rclient client.Client, cr, prevCR *v rtCh := make(chan *returnValue) shardCtx, cancel := context.WithCancel(ctx) owner := cr.AsOwner() - updateShard := func(num int) { + updateShard := func(num int32) { var rv returnValue defer func() { rtCh <- &rv @@ -233,13 +232,10 @@ func createOrUpdateApp(ctx context.Context, rclient client.Client, cr, prevCR *v return } selectorLabels := maps.Clone(newApp.Spec.Selector.MatchLabels) - opts := reconcile.STSOptions{ - HasClaim: len(newApp.Spec.VolumeClaimTemplates) > 0, - SelectorLabels: func() map[string]string { - return selectorLabels - }, + o := reconcile.StatefulSetOpts{ + SelectorLabels: selectorLabels, } - if err := reconcile.StatefulSet(shardCtx, rclient, opts, newApp, prevApp, &owner); err != nil { + if err := reconcile.StatefulSet(shardCtx, rclient, newApp, prevApp, &owner, &o); err != nil { rv.err = err return } @@ -279,7 +275,7 @@ func createOrUpdateApp(ctx context.Context, rclient client.Client, cr, prevCR *v return nil } -func getShard(cr *vmv1.VMAnomaly, appTpl *appsv1.StatefulSet, num int) (*appsv1.StatefulSet, error) { +func getShard(cr *vmv1.VMAnomaly, appTpl *appsv1.StatefulSet, num int32) (*appsv1.StatefulSet, error) { if appTpl == nil || !cr.IsSharded() { return appTpl, nil } diff --git a/internal/controller/operator/factory/vmauth/vmauth.go b/internal/controller/operator/factory/vmauth/vmauth.go index 91854033e..e33fe40e9 100644 --- a/internal/controller/operator/factory/vmauth/vmauth.go +++ b/internal/controller/operator/factory/vmauth/vmauth.go @@ -110,7 +110,14 @@ func CreateOrUpdate(ctx context.Context, cr *vmv1beta1.VMAuth, rclient client.Cl if err != nil { return fmt.Errorf("cannot build new deploy for vmauth: %w", err) } - if err := reconcile.Deployment(ctx, rclient, newDeploy, prevDeploy, cr.Spec.HPA != nil, &owner); err != nil { + o := reconcile.DeploymentOpts{ + PatchSpec: func(existingSpec, newSpec *appsv1.DeploymentSpec) { + if cr.Spec.HPA != nil { + newSpec.Replicas = existingSpec.Replicas + } + }, + } + if err := reconcile.Deployment(ctx, rclient, newDeploy, prevDeploy, &owner, &o); err != nil { return fmt.Errorf("cannot reconcile vmauth deployment: %w", err) } if prevCR != nil { diff --git a/internal/controller/operator/factory/vmcluster/vmcluster.go b/internal/controller/operator/factory/vmcluster/vmcluster.go index 6bd119d1e..82bd12a53 100644 --- a/internal/controller/operator/factory/vmcluster/vmcluster.go +++ b/internal/controller/operator/factory/vmcluster/vmcluster.go @@ -165,20 +165,16 @@ func createOrUpdateVMSelect(ctx context.Context, rclient client.Client, cr, prev return err } owner := cr.AsOwner() - stsOpts := reconcile.STSOptions{ - HasClaim: len(newSts.Spec.VolumeClaimTemplates) > 0, - SelectorLabels: func() map[string]string { - return cr.SelectorLabels(vmv1beta1.ClusterComponentSelect) - }, - HPA: cr.Spec.VMSelect.HPA, - UpdateReplicaCount: func(count *int32) { - if cr.Spec.VMSelect.HPA != nil && count != nil { - cr.Spec.VMSelect.ReplicaCount = count + o := reconcile.StatefulSetOpts{ + SelectorLabels: cr.SelectorLabels(vmv1beta1.ClusterComponentSelect), + UpdateBehavior: cr.Spec.VMSelect.RollingUpdateStrategyBehavior, + PatchSpec: func(existingSpec, newSpec *appsv1.StatefulSetSpec) { + if cr.Spec.VMSelect.HPA != nil { + newSpec.Replicas = existingSpec.Replicas } }, - UpdateBehavior: cr.Spec.VMSelect.RollingUpdateStrategyBehavior, } - return reconcile.StatefulSet(ctx, rclient, stsOpts, newSts, prevSts, &owner) + return reconcile.StatefulSet(ctx, rclient, newSts, prevSts, &owner, &o) } func buildVMSelectService(cr *vmv1beta1.VMCluster) *corev1.Service { @@ -316,7 +312,14 @@ func createOrUpdateVMInsert(ctx context.Context, rclient client.Client, cr, prev return err } owner := cr.AsOwner() - return reconcile.Deployment(ctx, rclient, newDeployment, prevDeploy, cr.Spec.VMInsert.HPA != nil, &owner) + o := reconcile.DeploymentOpts{ + PatchSpec: func(existingSpec, newSpec *appsv1.DeploymentSpec) { + if cr.Spec.VMInsert.HPA != nil { + newSpec.Replicas = existingSpec.Replicas + } + }, + } + return reconcile.Deployment(ctx, rclient, newDeployment, prevDeploy, &owner, &o) } func buildVMInsertService(cr *vmv1beta1.VMCluster) *corev1.Service { @@ -414,15 +417,17 @@ func createOrUpdateVMStorage(ctx context.Context, rclient client.Client, cr, pre return err } - stsOpts := reconcile.STSOptions{ - HasClaim: len(newSts.Spec.VolumeClaimTemplates) > 0, - SelectorLabels: func() map[string]string { - return cr.SelectorLabels(vmv1beta1.ClusterComponentStorage) - }, + o := reconcile.StatefulSetOpts{ + SelectorLabels: cr.SelectorLabels(vmv1beta1.ClusterComponentStorage), UpdateBehavior: cr.Spec.VMStorage.RollingUpdateStrategyBehavior, + PatchSpec: func(existingSpec, newSpec *appsv1.StatefulSetSpec) { + if cr.Spec.VMStorage.HPA != nil { + newSpec.Replicas = existingSpec.Replicas + } + }, } owner := cr.AsOwner() - return reconcile.StatefulSet(ctx, rclient, stsOpts, newSts, prevSts, &owner) + return reconcile.StatefulSet(ctx, rclient, newSts, prevSts, &owner, &o) } func buildVMStorageService(cr *vmv1beta1.VMCluster) *corev1.Service { @@ -1624,7 +1629,7 @@ func createOrUpdateVMAuthLB(ctx context.Context, rclient client.Client, cr, prev return fmt.Errorf("cannot build prev deployment for vmauth loadbalancing: %w", err) } } - if err := reconcile.Deployment(ctx, rclient, lbDep, prevLB, false, &owner); err != nil { + if err := reconcile.Deployment(ctx, rclient, lbDep, prevLB, &owner, nil); err != nil { return fmt.Errorf("cannot reconcile vmauth lb deployment: %w", err) } if err := createOrUpdateVMAuthLBService(ctx, rclient, cr, prevCR); err != nil { diff --git a/internal/controller/operator/factory/vmsingle/vmsingle.go b/internal/controller/operator/factory/vmsingle/vmsingle.go index a5b229c37..16e059118 100644 --- a/internal/controller/operator/factory/vmsingle/vmsingle.go +++ b/internal/controller/operator/factory/vmsingle/vmsingle.go @@ -110,7 +110,7 @@ func CreateOrUpdate(ctx context.Context, cr *vmv1beta1.VMSingle, rclient client. return fmt.Errorf("cannot generate new deploy for vmsingle: %w", err) } - return reconcile.Deployment(ctx, rclient, newDeploy, prevDeploy, false, &owner) + return reconcile.Deployment(ctx, rclient, newDeploy, prevDeploy, &owner, nil) } func newDeploy(ctx context.Context, cr *vmv1beta1.VMSingle) (*appsv1.Deployment, error) { diff --git a/internal/controller/operator/factory/vtcluster/insert.go b/internal/controller/operator/factory/vtcluster/insert.go index 76c50d46a..5a8ea9422 100644 --- a/internal/controller/operator/factory/vtcluster/insert.go +++ b/internal/controller/operator/factory/vtcluster/insert.go @@ -74,7 +74,14 @@ func createOrUpdateVTInsertDeployment(ctx context.Context, rclient client.Client return err } owner := cr.AsOwner() - return reconcile.Deployment(ctx, rclient, newDeployment, prevDeploy, cr.Spec.Insert.HPA != nil, &owner) + o := reconcile.DeploymentOpts{ + PatchSpec: func(existingSpec, newSpec *appsv1.DeploymentSpec) { + if cr.Spec.Insert.HPA != nil { + newSpec.Replicas = existingSpec.Replicas + } + }, + } + return reconcile.Deployment(ctx, rclient, newDeployment, prevDeploy, &owner, &o) } func buildVTInsertDeployment(cr *vmv1.VTCluster) (*appsv1.Deployment, error) { diff --git a/internal/controller/operator/factory/vtcluster/select.go b/internal/controller/operator/factory/vtcluster/select.go index 3a06b2488..214bb69f5 100644 --- a/internal/controller/operator/factory/vtcluster/select.go +++ b/internal/controller/operator/factory/vtcluster/select.go @@ -181,7 +181,14 @@ func createOrUpdateVTSelectDeployment(ctx context.Context, rclient client.Client return err } owner := cr.AsOwner() - return reconcile.Deployment(ctx, rclient, newDep, prevDep, cr.Spec.Select.HPA != nil, &owner) + o := reconcile.DeploymentOpts{ + PatchSpec: func(existingSpec, newSpec *appsv1.DeploymentSpec) { + if cr.Spec.Select.HPA != nil { + newSpec.Replicas = existingSpec.Replicas + } + }, + } + return reconcile.Deployment(ctx, rclient, newDep, prevDep, &owner, &o) } func buildVTSelectDeployment(cr *vmv1.VTCluster) (*appsv1.Deployment, error) { diff --git a/internal/controller/operator/factory/vtcluster/storage.go b/internal/controller/operator/factory/vtcluster/storage.go index 7b40f4d98..e6ec80418 100644 --- a/internal/controller/operator/factory/vtcluster/storage.go +++ b/internal/controller/operator/factory/vtcluster/storage.go @@ -163,15 +163,17 @@ func createOrUpdateVTStorageSTS(ctx context.Context, rclient client.Client, cr, return err } - stsOpts := reconcile.STSOptions{ - HasClaim: len(newSts.Spec.VolumeClaimTemplates) > 0, - SelectorLabels: func() map[string]string { - return cr.SelectorLabels(vmv1beta1.ClusterComponentStorage) - }, + o := reconcile.StatefulSetOpts{ + SelectorLabels: cr.SelectorLabels(vmv1beta1.ClusterComponentStorage), UpdateBehavior: cr.Spec.Storage.RollingUpdateStrategyBehavior, + PatchSpec: func(existingSpec, newSpec *appsv1.StatefulSetSpec) { + if cr.Spec.Storage.HPA != nil { + newSpec.Replicas = existingSpec.Replicas + } + }, } owner := cr.AsOwner() - return reconcile.StatefulSet(ctx, rclient, stsOpts, newSts, prevSts, &owner) + return reconcile.StatefulSet(ctx, rclient, newSts, prevSts, &owner, &o) } func buildVTStorageSTSSpec(cr *vmv1.VTCluster) (*appsv1.StatefulSet, error) { diff --git a/internal/controller/operator/factory/vtcluster/vmauth_lb.go b/internal/controller/operator/factory/vtcluster/vmauth_lb.go index 90e7d5a4c..0c2435ed7 100644 --- a/internal/controller/operator/factory/vtcluster/vmauth_lb.go +++ b/internal/controller/operator/factory/vtcluster/vmauth_lb.go @@ -42,7 +42,7 @@ func createOrUpdateVMAuthLB(ctx context.Context, rclient client.Client, cr, prev return fmt.Errorf("cannot build prev deployment for vmauth loadbalancing: %w", err) } } - if err := reconcile.Deployment(ctx, rclient, lbDep, prevLB, false, &owner); err != nil { + if err := reconcile.Deployment(ctx, rclient, lbDep, prevLB, &owner, nil); err != nil { return fmt.Errorf("cannot reconcile vmauth lb deployment: %w", err) } if err := createOrUpdateVMAuthLBService(ctx, rclient, cr, prevCR); err != nil { diff --git a/internal/controller/operator/factory/vtsingle/vtsingle.go b/internal/controller/operator/factory/vtsingle/vtsingle.go index f5152b8d9..061ef7d76 100644 --- a/internal/controller/operator/factory/vtsingle/vtsingle.go +++ b/internal/controller/operator/factory/vtsingle/vtsingle.go @@ -108,7 +108,7 @@ func CreateOrUpdate(ctx context.Context, rclient client.Client, cr *vmv1.VTSingl return fmt.Errorf("cannot generate new deploy for vtsingle: %w", err) } - return reconcile.Deployment(ctx, rclient, newDeploy, prevDeploy, false, &owner) + return reconcile.Deployment(ctx, rclient, newDeploy, prevDeploy, &owner, nil) } func newDeployment(r *vmv1.VTSingle) (*appsv1.Deployment, error) { diff --git a/test/e2e/vmagent_test.go b/test/e2e/vmagent_test.go index 35f934d35..90999c023 100644 --- a/test/e2e/vmagent_test.go +++ b/test/e2e/vmagent_test.go @@ -543,7 +543,7 @@ var _ = Describe("test vmagent Controller", Label("vm", "agent", "vmagent"), fun testStep{ modify: func(cr *vmv1beta1.VMAgent) { cr.Spec.ReplicaCount = ptr.To[int32](1) - cr.Spec.ShardCount = ptr.To(2) + cr.Spec.ShardCount = ptr.To[int32](2) cr.Spec.StatefulMode = true cr.Spec.IngestOnlyMode = ptr.To(true) }, diff --git a/test/e2e/vmanomaly_test.go b/test/e2e/vmanomaly_test.go index 0a4beddea..a6c4dedb6 100644 --- a/test/e2e/vmanomaly_test.go +++ b/test/e2e/vmanomaly_test.go @@ -360,7 +360,7 @@ var _ = Describe("test vmanomaly Controller", Label("vm", "anomaly", "enterprise testStep{ modify: func(cr *vmv1.VMAnomaly) { cr.Spec.ReplicaCount = ptr.To[int32](1) - cr.Spec.ShardCount = ptr.To(2) + cr.Spec.ShardCount = ptr.To[int32](2) }, verify: func(cr *vmv1.VMAnomaly) { var createdSts appsv1.StatefulSet From 5e386b7b5c50c7dbd0ed5c5a0309310a48602414 Mon Sep 17 00:00:00 2001 From: Andrii Chubatiuk Date: Mon, 30 Mar 2026 09:37:48 +0300 Subject: [PATCH 06/22] vmalertmanager: do not ignore tracing config, when to alertmanagerconfig CRs are collected (#1984) --- docs/CHANGELOG.md | 5 ++ .../operator/factory/vmalertmanager/config.go | 83 ++++++++++--------- .../factory/vmalertmanager/config_test.go | 60 ++++++++++++++ 3 files changed, 108 insertions(+), 40 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index a0be9ef59..61f5a0242 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -13,6 +13,11 @@ aliases: ## tip +* FEATURE: [vmagent](https://docs.victoriametrics.com/operator/resources/vmagent/): introduce statefulRollingUpdateStrategyBehavior to allow managing VMAgent update strategy in a statefulMode. See [#1987](https://github.com/VictoriaMetrics/operator/issues/1987). + +* BUGFIX: [vmoperator](https://docs.victoriametrics.com/operator/): wait till PVC resize finished. See [#1970](https://github.com/VictoriaMetrics/operator/issues/1983). +* BUGFIX: [vmalertmanager](https://docs.victoriametrics.com/operator/resources/vmalertmanager/): fixed ignored tracing config, when no alertmanagerconfig CRs collected. See [#1983](https://github.com/VictoriaMetrics/operator/issues/1983). + ## [v0.68.3](https://github.com/VictoriaMetrics/operator/releases/tag/v0.68.3) **Release date:** 16 March 2026 diff --git a/internal/controller/operator/factory/vmalertmanager/config.go b/internal/controller/operator/factory/vmalertmanager/config.go index de89edce5..b424316df 100644 --- a/internal/controller/operator/factory/vmalertmanager/config.go +++ b/internal/controller/operator/factory/vmalertmanager/config.go @@ -18,13 +18,9 @@ type parsedObjects struct { } func (pos *parsedObjects) buildConfig(cr *vmv1beta1.VMAlertmanager, baseCfg []byte, ac *build.AssetsCache) ([]byte, error) { - if len(pos.configs.All()) == 0 { + if len(pos.configs.All()) == 0 && cr.Spec.TracingConfig == nil { return baseCfg, nil } - var globalConfigOpts globalAlertmanagerConfig - if err := yaml.Unmarshal(baseCfg, &globalConfigOpts); err != nil { - return nil, fmt.Errorf("cannot parse global config options: %w", err) - } var baseYAMLCfg alertmanagerConfig if err := yaml.Unmarshal(baseCfg, &baseYAMLCfg); err != nil { return nil, fmt.Errorf("cannot parse base cfg: %w", err) @@ -63,49 +59,56 @@ func (pos *parsedObjects) buildConfig(cr *vmv1beta1.VMAlertmanager, baseCfg []by }) } } - var subRoutes []yaml.MapSlice - var timeIntervals []yaml.MapSlice - pos.configs.ForEachCollectSkipInvalid(func(cfg *vmv1beta1.VMAlertmanagerConfig) error { - if !build.MustSkipRuntimeValidation() { - if err := cfg.Validate(); err != nil { - return err - } + + if len(pos.configs.All()) > 0 { + var globalConfigOpts globalAlertmanagerConfig + if err := yaml.Unmarshal(baseCfg, &globalConfigOpts); err != nil { + return nil, fmt.Errorf("cannot parse global config options: %w", err) } - var receiverCfgs []yaml.MapSlice - for _, receiver := range cfg.Spec.Receivers { - receiverCfg, err := buildReceiver(cfg, receiver, &globalConfigOpts, ac) - if err != nil { - return err + var subRoutes []yaml.MapSlice + var timeIntervals []yaml.MapSlice + pos.configs.ForEachCollectSkipInvalid(func(cfg *vmv1beta1.VMAlertmanagerConfig) error { + if !build.MustSkipRuntimeValidation() { + if err := cfg.Validate(); err != nil { + return err + } } - if len(receiverCfg) > 0 { - receiverCfgs = append(receiverCfgs, receiverCfg) + var receiverCfgs []yaml.MapSlice + for _, receiver := range cfg.Spec.Receivers { + receiverCfg, err := buildReceiver(cfg, receiver, &globalConfigOpts, ac) + if err != nil { + return err + } + if len(receiverCfg) > 0 { + receiverCfgs = append(receiverCfgs, receiverCfg) + } } - } - mtis, err := buildGlobalTimeIntervals(cfg) - if err != nil { - return err - } - if cfg.Spec.Route != nil { - route, err := buildRoute(cfg, cfg.Spec.Route, true, cr) + mtis, err := buildGlobalTimeIntervals(cfg) if err != nil { return err } - subRoutes = append(subRoutes, route) - } - baseYAMLCfg.Receivers = append(baseYAMLCfg.Receivers, receiverCfgs...) - for _, rule := range cfg.Spec.InhibitRules { - baseYAMLCfg.InhibitRules = append(baseYAMLCfg.InhibitRules, buildInhibitRule(cfg.Namespace, rule, !cr.Spec.DisableNamespaceMatcher)) + if cfg.Spec.Route != nil { + route, err := buildRoute(cfg, cfg.Spec.Route, true, cr) + if err != nil { + return err + } + subRoutes = append(subRoutes, route) + } + baseYAMLCfg.Receivers = append(baseYAMLCfg.Receivers, receiverCfgs...) + for _, rule := range cfg.Spec.InhibitRules { + baseYAMLCfg.InhibitRules = append(baseYAMLCfg.InhibitRules, buildInhibitRule(cfg.Namespace, rule, !cr.Spec.DisableNamespaceMatcher)) + } + if len(mtis) > 0 { + timeIntervals = append(timeIntervals, mtis...) + } + return nil + }) + if len(subRoutes) > 0 { + baseYAMLCfg.Route.Routes = append(baseYAMLCfg.Route.Routes, subRoutes...) } - if len(mtis) > 0 { - timeIntervals = append(timeIntervals, mtis...) + if len(timeIntervals) > 0 { + baseYAMLCfg.TimeIntervals = append(baseYAMLCfg.TimeIntervals, timeIntervals...) } - return nil - }) - if len(subRoutes) > 0 { - baseYAMLCfg.Route.Routes = append(baseYAMLCfg.Route.Routes, subRoutes...) - } - if len(timeIntervals) > 0 { - baseYAMLCfg.TimeIntervals = append(baseYAMLCfg.TimeIntervals, timeIntervals...) } if cr.Spec.TracingConfig != nil { tracingCfg, err := buildTracingConfig(cr, ac) diff --git a/internal/controller/operator/factory/vmalertmanager/config_test.go b/internal/controller/operator/factory/vmalertmanager/config_test.go index 28bbb2614..25fa59207 100644 --- a/internal/controller/operator/factory/vmalertmanager/config_test.go +++ b/internal/controller/operator/factory/vmalertmanager/config_test.go @@ -67,6 +67,66 @@ func TestBuildConfig(t *testing.T) { assert.Equal(t, o.want, string(data)) } + // tracing without discovered objects + f(opts{ + cr: &vmv1beta1.VMAlertmanager{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "default", + }, + Spec: vmv1beta1.VMAlertmanagerSpec{ + EnforcedNamespaceLabel: "alert-namespace", + ConfigNamespaceSelector: &metav1.LabelSelector{}, + TracingConfig: &vmv1beta1.VMAlertmanagerTracingConfig{ + Endpoint: "http://example.com", + TLSConfig: &vmv1beta1.TLSClientConfig{ + CASecretRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "ca", + }, + Key: "key", + }, + }, + Compression: "gzip", + HTTPHeaders: map[string]string{ + "name": "value", + }, + }, + }, + }, + baseCfg: []byte(`global: + time_out: 1min + smtp_smarthost: some:443 +`), + predefinedObjects: []runtime.Object{ + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "ca", + Namespace: "default", + }, + Data: map[string][]byte{ + "key": []byte("value"), + }, + }, + }, + want: `global: + smtp_smarthost: some:443 + time_out: 1min +route: + receiver: blackhole +receivers: +- name: blackhole +templates: [] +tracing: + tls_config: + ca_file: /etc/alertmanager/tls_assets/default_ca_key + compression: gzip + http_headers: + name: value + endpoint: http://example.com +`, + }) + // override the top namespace matcher f(opts{ cr: &vmv1beta1.VMAlertmanager{ From 060ec3535e83c71a38cbb6369b812eb6dbdbb75b Mon Sep 17 00:00:00 2001 From: Andrii Chubatiuk Date: Mon, 30 Mar 2026 10:08:01 +0300 Subject: [PATCH 07/22] vmagent: apply scrapeclass relabellings before job ones (#2003) Signed-off-by: Vadim Rutkovsky Co-authored-by: Vadim Rutkovsky --- docs/CHANGELOG.md | 3 ++- .../factory/vmagent/servicescrape_test.go | 24 +++++++++---------- .../factory/vmagent/vmagent_scrapeconfig.go | 4 ++-- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 61f5a0242..dab965407 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -15,8 +15,9 @@ aliases: * FEATURE: [vmagent](https://docs.victoriametrics.com/operator/resources/vmagent/): introduce statefulRollingUpdateStrategyBehavior to allow managing VMAgent update strategy in a statefulMode. See [#1987](https://github.com/VictoriaMetrics/operator/issues/1987). -* BUGFIX: [vmoperator](https://docs.victoriametrics.com/operator/): wait till PVC resize finished. See [#1970](https://github.com/VictoriaMetrics/operator/issues/1983). +* BUGFIX: [vmoperator](https://docs.victoriametrics.com/operator/): wait till PVC resize finished. See [#1970](https://github.com/VictoriaMetrics/operator/issues/1970). * BUGFIX: [vmalertmanager](https://docs.victoriametrics.com/operator/resources/vmalertmanager/): fixed ignored tracing config, when no alertmanagerconfig CRs collected. See [#1983](https://github.com/VictoriaMetrics/operator/issues/1983). +* BUGFIX: [vmagent](https://docs.victoriametrics.com/operator/resources/vmagent/): apply scrape class relabellings before job ones. See [#1997](https://github.com/VictoriaMetrics/operator/issues/1997). ## [v0.68.3](https://github.com/VictoriaMetrics/operator/releases/tag/v0.68.3) **Release date:** 16 March 2026 diff --git a/internal/controller/operator/factory/vmagent/servicescrape_test.go b/internal/controller/operator/factory/vmagent/servicescrape_test.go index 195a92a2b..9e4bcbb8e 100644 --- a/internal/controller/operator/factory/vmagent/servicescrape_test.go +++ b/internal/controller/operator/factory/vmagent/servicescrape_test.go @@ -1257,14 +1257,14 @@ relabel_configs: replacement: ${1} - target_label: endpoint replacement: "8080" -- source_labels: - - __meta_kubernetes_namespace - target_label: namespace - action: replace - source_labels: - __meta_kubernetes_pod_app_name target_label: app action: replace +- source_labels: + - __meta_kubernetes_namespace + target_label: namespace + action: replace `, }) @@ -1386,14 +1386,14 @@ relabel_configs: replacement: ${1} - target_label: endpoint replacement: "8080" -- source_labels: - - __meta_kubernetes_pod_container_name - target_label: container - action: replace - source_labels: - __meta_kubernetes_pod_node_name target_label: node action: replace +- source_labels: + - __meta_kubernetes_pod_container_name + target_label: container + action: replace tls_config: ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt cert_file: /etc/vmagent-tls/certs/default_tls-secret_cert @@ -1692,10 +1692,6 @@ relabel_configs: - target_label: endpoint replacement: "9090" metric_relabel_configs: -- source_labels: - - instance - target_label: endpoint_instance - action: replace - source_labels: - __name__ regex: go_.* @@ -1703,6 +1699,10 @@ metric_relabel_configs: - target_label: scrape_class replacement: metrics-class action: replace +- source_labels: + - instance + target_label: endpoint_instance + action: replace `, }) diff --git a/internal/controller/operator/factory/vmagent/vmagent_scrapeconfig.go b/internal/controller/operator/factory/vmagent/vmagent_scrapeconfig.go index 4f66bba4b..e8fc60246 100644 --- a/internal/controller/operator/factory/vmagent/vmagent_scrapeconfig.go +++ b/internal/controller/operator/factory/vmagent/vmagent_scrapeconfig.go @@ -759,8 +759,8 @@ func mergeEndpointRelabelingsWithScrapeClass(ers *vmv1beta1.EndpointRelabelings, if ers == nil { panic("BUG: ers cannot be nil") } - ers.RelabelConfigs = append(ers.RelabelConfigs, scrapeClass.RelabelConfigs...) - ers.MetricRelabelConfigs = append(ers.MetricRelabelConfigs, scrapeClass.MetricRelabelConfigs...) + ers.RelabelConfigs = append(scrapeClass.RelabelConfigs, ers.RelabelConfigs...) + ers.MetricRelabelConfigs = append(scrapeClass.MetricRelabelConfigs, ers.MetricRelabelConfigs...) } func mergeAuthorizationWithScrapeClass(authz *vmv1beta1.Authorization, scrapeClass *vmv1beta1.ScrapeClass) *vmv1beta1.Authorization { From a4ef3aad9f55060c1ac41ddb0b3a920d921eb46e Mon Sep 17 00:00:00 2001 From: Vadim Rutkovsky Date: Tue, 17 Mar 2026 11:50:30 +0100 Subject: [PATCH 08/22] Requeue zone update when context is cancelled (#1965) * fix: add cancellation reason for graceful shutdown, retry other requests * make waitForEmptyPQ return no error --------- Co-authored-by: Andrii Chubatiuk --- cmd/main.go | 5 +- internal/controller/operator/controllers.go | 9 +++ .../controller/operator/controllers_test.go | 79 +++++++++++++++++++ .../operator/factory/vmdistributed/zone.go | 35 +++----- .../factory/vmdistributed/zone_test.go | 5 +- test/e2e/suite/suite.go | 7 +- 6 files changed, 107 insertions(+), 33 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index 79c63034c..dae0deeaa 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -23,6 +23,7 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/manager/signals" + "github.com/VictoriaMetrics/operator/internal/controller/operator" "github.com/VictoriaMetrics/operator/internal/manager" ) @@ -31,11 +32,11 @@ var ( ) func main() { - ctx, cancel := context.WithCancel(context.Background()) + ctx, cancel := context.WithCancelCause(context.Background()) stop := signals.SetupSignalHandler() go func() { <-stop.Done() - cancel() + cancel(operator.ErrShutdown) }() err := manager.RunManager(ctx) diff --git a/internal/controller/operator/controllers.go b/internal/controller/operator/controllers.go index 70c6a9a84..b455c17c7 100644 --- a/internal/controller/operator/controllers.go +++ b/internal/controller/operator/controllers.go @@ -90,6 +90,9 @@ type parsingError struct { controller string } +// ErrShutdown is a custom error returned as a cause of operator context cancel +var ErrShutdown = fmt.Errorf("graceful shutdown, exiting") + func (pe *parsingError) Error() string { return fmt.Sprintf("parsing object error for object controller=%q: %q", pe.controller, pe.origin) @@ -128,6 +131,9 @@ func handleReconcileErr[T client.Object, ST reconcile.StatusWithMetadata[STC], S switch { case errors.Is(err, context.Canceled): contextCancelErrorsTotal.Inc() + if !errors.Is(context.Cause(ctx), ErrShutdown) { + originResult.RequeueAfter = time.Second * 5 + } return originResult, nil case errors.As(err, &pe): namespacedName := "unknown" @@ -199,6 +205,9 @@ func handleReconcileErrWithoutStatus( switch { case errors.Is(err, context.Canceled): contextCancelErrorsTotal.Inc() + if !errors.Is(context.Cause(ctx), ErrShutdown) { + originResult.RequeueAfter = time.Second * 5 + } return originResult, nil case errors.As(err, &pe): parseObjectErrorsTotal.WithLabelValues(pe.controller, "unknown").Inc() diff --git a/internal/controller/operator/controllers_test.go b/internal/controller/operator/controllers_test.go index 572a11012..9746b4b07 100644 --- a/internal/controller/operator/controllers_test.go +++ b/internal/controller/operator/controllers_test.go @@ -2,12 +2,16 @@ package operator import ( "context" + "errors" + "fmt" "testing" + "time" "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" vmv1beta1 "github.com/VictoriaMetrics/operator/api/operator/v1beta1" @@ -314,3 +318,78 @@ func TestIsSelectorsMatchesTargetCRD(t *testing.T) { isMatch: true, }) } + +func TestHandleReconcileErrWithoutStatus(t *testing.T) { + type opts struct { + err error + origin ctrl.Result + wantResult ctrl.Result + wantErr error + } + + f := func(ctx context.Context, o opts) { + t.Helper() + got, err := handleReconcileErrWithoutStatus(ctx, nil, nil, o.origin, o.err) + assert.Equal(t, o.wantErr, err) + assert.Equal(t, o.wantResult, got) + } + + // no error + f(context.Background(), opts{ + err: nil, + origin: ctrl.Result{RequeueAfter: 10}, + wantResult: ctrl.Result{RequeueAfter: 10}, + wantErr: nil, + }) + + // context canceled + f(context.Background(), opts{ + err: context.Canceled, + origin: ctrl.Result{}, + wantResult: ctrl.Result{RequeueAfter: time.Second * 5}, + wantErr: nil, + }) + + // context canceled with ErrShutdown + shutdownCtx, shutdownCancel := context.WithCancelCause(context.Background()) + shutdownCancel(ErrShutdown) + f(shutdownCtx, opts{ + err: fmt.Errorf("wrapped: %w", errors.Join(context.Canceled, ErrShutdown)), + origin: ctrl.Result{}, + wantResult: ctrl.Result{}, + wantErr: nil, + }) +} + +func TestHandleReconcileErr(t *testing.T) { + type opts struct { + err error + origin ctrl.Result + wantResult ctrl.Result + wantErr error + } + + f := func(o opts) { + t.Helper() + got, err := handleReconcileErr(context.Background(), nil, (*vmv1beta1.VMCluster)(nil), o.origin, o.err) + assert.Equal(t, o.wantErr, err) + assert.Equal(t, o.wantResult, got) + } + + // no error + f(opts{ + err: nil, + origin: ctrl.Result{RequeueAfter: 10}, + wantResult: ctrl.Result{RequeueAfter: 10}, + wantErr: nil, + }) + + // context canceled + f(opts{ + err: context.Canceled, + origin: ctrl.Result{}, + wantResult: ctrl.Result{RequeueAfter: time.Second * 5}, + wantErr: nil, + }) + +} diff --git a/internal/controller/operator/factory/vmdistributed/zone.go b/internal/controller/operator/factory/vmdistributed/zone.go index d3bf82923..bdb695752 100644 --- a/internal/controller/operator/factory/vmdistributed/zone.go +++ b/internal/controller/operator/factory/vmdistributed/zone.go @@ -2,7 +2,6 @@ package vmdistributed import ( "context" - "errors" "fmt" "net/http" "net/url" @@ -195,8 +194,9 @@ func (zs *zones) upgrade(ctx context.Context, rclient client.Client, cr *vmv1alp } if needsLBUpdate { // wait for empty persistent queue - if err := zs.waitForEmptyPQ(ctx, rclient, defaultMetricsCheckInterval, i); err != nil { - return fmt.Errorf("zone=%s: failed to wait till VMCluster=%s queue is empty: %w", item, nsnCluster.String(), err) + zs.waitForEmptyPQ(ctx, rclient, defaultMetricsCheckInterval, i) + if ctx.Err() != nil { + return fmt.Errorf("zone=%s: failed to wait till VMCluster=%s queue is empty", item, nsnCluster.String()) } // excluding zone from VMAuth LB @@ -217,8 +217,9 @@ func (zs *zones) upgrade(ctx context.Context, rclient client.Client, cr *vmv1alp } // wait for empty persistent queue - if err := zs.waitForEmptyPQ(ctx, rclient, defaultMetricsCheckInterval, i); err != nil { - return fmt.Errorf("zone=%s: failed to wait till VMAgent queue for VMCluster=%s is drained: %w", item, nsnCluster.String(), err) + zs.waitForEmptyPQ(ctx, rclient, defaultMetricsCheckInterval, i) + if ctx.Err() != nil { + return fmt.Errorf("zone=%s: failed to wait till VMAgent queue for VMCluster=%s is drained", item, nsnCluster.String()) } // restore zone in VMAuth LB @@ -290,7 +291,7 @@ func getMetricsAddrs(ctx context.Context, rclient client.Client, vmAgent *vmv1be return addrs } -func (zs *zones) waitForEmptyPQ(ctx context.Context, rclient client.Client, interval time.Duration, clusterIdx int) error { +func (zs *zones) waitForEmptyPQ(ctx context.Context, rclient client.Client, interval time.Duration, clusterIdx int) { vmCluster := zs.vmclusters[clusterIdx] clusterURLHash := fmt.Sprintf("%016X", xxhash.Sum64([]byte(vmCluster.GetRemoteWriteURL()))) @@ -324,17 +325,6 @@ func (zs *zones) waitForEmptyPQ(ctx context.Context, rclient client.Client, inte } var wg sync.WaitGroup - var resultErr error - var once sync.Once - gctx, gcancel := context.WithCancel(ctx) - defer gcancel() - cancel := func(err error) { - once.Do(func() { - resultErr = err - gcancel() - }) - } - for i := range zs.vmagents { vmAgent := zs.vmagents[i] if vmAgent.CreationTimestamp.IsZero() { @@ -344,7 +334,7 @@ func (zs *zones) waitForEmptyPQ(ctx context.Context, rclient client.Client, inte Name: vmAgent.Name, Namespace: vmAgent.Namespace, } - m := newManager(gctx) + m := newManager(ctx) wg.Go(func() { wait.UntilWithContext(m.ctx, func(ctx context.Context) { addrs := getMetricsAddrs(ctx, rclient, vmAgent) @@ -356,10 +346,7 @@ func (zs *zones) waitForEmptyPQ(ctx context.Context, rclient client.Client, inte pctx := m.add(addr) wg.Go(func() { if err := pollMetrics(pctx, nsn, addr); err != nil { - if !errors.Is(err, context.Canceled) { - cancel(err) - return - } + return } m.stop(addr) }) @@ -374,10 +361,6 @@ func (zs *zones) waitForEmptyPQ(ctx context.Context, rclient client.Client, inte }) } wg.Wait() - if resultErr != nil { - return fmt.Errorf("failed to wait for VMAgent metrics: %w", resultErr) - } - return nil } func newManager(ctx context.Context) *manager { diff --git a/internal/controller/operator/factory/vmdistributed/zone_test.go b/internal/controller/operator/factory/vmdistributed/zone_test.go index 113de2315..3cf3b95c1 100644 --- a/internal/controller/operator/factory/vmdistributed/zone_test.go +++ b/internal/controller/operator/factory/vmdistributed/zone_test.go @@ -194,7 +194,8 @@ func TestWaitForEmptyPQ(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), o.timeout) defer cancel() - err = zs.waitForEmptyPQ(ctx, rclient, 1*time.Second, 0) + zs.waitForEmptyPQ(ctx, rclient, 1*time.Second, 0) + err = ctx.Err() if len(o.errMsg) > 0 { assert.Error(t, err) assert.Contains(t, err.Error(), o.errMsg) @@ -241,6 +242,6 @@ func TestWaitForEmptyPQ(t *testing.T) { fmt.Fprintf(w, `%s{path="/tmp/1_EF46DB3751D8E999"} 0`, vmAgentQueueMetricName) }, timeout: 500 * time.Millisecond, - errMsg: "failed to wait for VMAgent metrics", + errMsg: "context deadline exceeded", }) } diff --git a/test/e2e/suite/suite.go b/test/e2e/suite/suite.go index ea17e41ba..db899e65b 100644 --- a/test/e2e/suite/suite.go +++ b/test/e2e/suite/suite.go @@ -32,13 +32,14 @@ import ( vmv1 "github.com/VictoriaMetrics/operator/api/operator/v1" vmv1alpha1 "github.com/VictoriaMetrics/operator/api/operator/v1alpha1" vmv1beta1 "github.com/VictoriaMetrics/operator/api/operator/v1beta1" + "github.com/VictoriaMetrics/operator/internal/controller/operator" "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/build" "github.com/VictoriaMetrics/operator/internal/manager" ) var ( testEnv *envtest.Environment - cancelManager context.CancelFunc + cancelManager context.CancelCauseFunc stopped = make(chan struct{}) ) @@ -172,7 +173,7 @@ func InitOperatorProcess(extraNamespaces ...string) []byte { "--health-probe-bind-address", "0", "--controller.maxConcurrentReconciles", "30", ) - ctx, cancel := context.WithCancel(context.Background()) + ctx, cancel := context.WithCancelCause(context.Background()) go func(ctx context.Context) { defer GinkgoRecover() err := manager.RunManager(ctx) @@ -187,7 +188,7 @@ func InitOperatorProcess(extraNamespaces ...string) []byte { // and cleanup resources func ShutdownOperatorProcess() { By("tearing down the test environment") - cancelManager() + cancelManager(operator.ErrShutdown) Eventually(stopped, 60, 2).Should(BeClosed()) Expect(testEnv.Stop()).ToNot(HaveOccurred()) From b1dd81db4aa4fa15aefa23a4abf8899a9bc8a628 Mon Sep 17 00:00:00 2001 From: Andrii Chubatiuk Date: Tue, 17 Mar 2026 14:06:07 +0200 Subject: [PATCH 09/22] controllers: use single error handling for all CRs (#1964) --- api/operator/v1/vlagent_types.go | 4 +- api/operator/v1/vlcluster_types.go | 4 +- api/operator/v1/vlsingle_types.go | 4 +- api/operator/v1/vmanomaly_types.go | 4 +- api/operator/v1/vtcluster_types.go | 4 +- api/operator/v1/vtsingle_types.go | 4 +- api/operator/v1alpha1/vmdistributed_types.go | 4 +- api/operator/v1beta1/vlogs_types.go | 4 +- api/operator/v1beta1/vmagent_types.go | 4 +- api/operator/v1beta1/vmalert_types.go | 4 +- api/operator/v1beta1/vmalertmanager_types.go | 4 +- .../v1beta1/vmalertmanagerconfig_types.go | 8 +++ api/operator/v1beta1/vmauth_types.go | 4 +- api/operator/v1beta1/vmcluster_types.go | 4 +- api/operator/v1beta1/vmnodescrape_types.go | 8 +++ api/operator/v1beta1/vmpodscrape_types.go | 8 +++ api/operator/v1beta1/vmprobe_types.go | 8 +++ api/operator/v1beta1/vmrule_types.go | 21 ++++++ api/operator/v1beta1/vmscrapeconfig_types.go | 8 +++ api/operator/v1beta1/vmservicescrape_types.go | 8 +++ api/operator/v1beta1/vmsingle_types.go | 4 +- api/operator/v1beta1/vmstaticscrape_types.go | 8 +++ api/operator/v1beta1/vmuser_types.go | 21 ++++++ internal/controller/operator/controllers.go | 67 ------------------- .../controller/operator/controllers_test.go | 50 +++----------- .../operator/factory/reconcile/reconcile.go | 4 +- .../operator/factory/reconcile/status.go | 4 +- .../controller/operator/vlagent_controller.go | 26 +++---- .../operator/vlcluster_controller.go | 26 +++---- .../controller/operator/vlogs_controller.go | 18 ++--- .../operator/vlsingle_controller.go | 26 +++---- .../controller/operator/vmagent_controller.go | 42 ++++++------ .../controller/operator/vmalert_controller.go | 30 ++++----- .../operator/vmalertmanager_controller.go | 28 ++++---- .../vmalertmanagerconfig_controller.go | 24 ++++--- .../operator/vmanomaly_controller.go | 26 +++---- .../controller/operator/vmauth_controller.go | 25 +++---- .../operator/vmcluster_controller.go | 28 ++++---- .../operator/vmdistributed_controller.go | 28 ++++---- .../operator/vmnodescrape_controller.go | 17 ++--- .../operator/vmpodscrape_controller.go | 17 ++--- .../controller/operator/vmprobe_controller.go | 17 ++--- .../controller/operator/vmrule_controller.go | 32 +++++---- .../operator/vmscrapeconfig_controller.go | 17 ++--- .../operator/vmservicescrape_controller.go | 17 ++--- .../operator/vmsingle_controller.go | 26 +++---- .../operator/vmstaticscrape_controller.go | 16 +++-- .../controller/operator/vmuser_controller.go | 32 +++++---- .../operator/vtcluster_controller.go | 26 +++---- .../operator/vtsingle_controller.go | 26 +++---- 50 files changed, 440 insertions(+), 409 deletions(-) diff --git a/api/operator/v1/vlagent_types.go b/api/operator/v1/vlagent_types.go index c3e951f35..efede59c8 100644 --- a/api/operator/v1/vlagent_types.go +++ b/api/operator/v1/vlagent_types.go @@ -288,8 +288,8 @@ type VLAgentStatus struct { } // GetStatusMetadata returns metadata for object status -func (cr *VLAgentStatus) GetStatusMetadata() *vmv1beta1.StatusMetadata { - return &cr.StatusMetadata +func (cr *VLAgent) GetStatusMetadata() *vmv1beta1.StatusMetadata { + return &cr.Status.StatusMetadata } // +genclient diff --git a/api/operator/v1/vlcluster_types.go b/api/operator/v1/vlcluster_types.go index 479487b6b..9b13feb5d 100644 --- a/api/operator/v1/vlcluster_types.go +++ b/api/operator/v1/vlcluster_types.go @@ -210,8 +210,8 @@ type VLClusterStatus struct { } // GetStatusMetadata returns metadata for object status -func (cr *VLClusterStatus) GetStatusMetadata() *vmv1beta1.StatusMetadata { - return &cr.StatusMetadata +func (cr *VLCluster) GetStatusMetadata() *vmv1beta1.StatusMetadata { + return &cr.Status.StatusMetadata } // VLInsert defines vlinsert component configuration at victoria-logs cluster diff --git a/api/operator/v1/vlsingle_types.go b/api/operator/v1/vlsingle_types.go index 42c4445b6..6ed4943b1 100644 --- a/api/operator/v1/vlsingle_types.go +++ b/api/operator/v1/vlsingle_types.go @@ -111,8 +111,8 @@ type VLSingleStatus struct { } // GetStatusMetadata returns metadata for object status -func (cr *VLSingleStatus) GetStatusMetadata() *vmv1beta1.StatusMetadata { - return &cr.StatusMetadata +func (cr *VLSingle) GetStatusMetadata() *vmv1beta1.StatusMetadata { + return &cr.Status.StatusMetadata } // VLSingle is fast, cost-effective and scalable logs database. diff --git a/api/operator/v1/vmanomaly_types.go b/api/operator/v1/vmanomaly_types.go index 1de93d1f2..0a94104b1 100644 --- a/api/operator/v1/vmanomaly_types.go +++ b/api/operator/v1/vmanomaly_types.go @@ -181,8 +181,8 @@ type VMAnomalyStatus struct { } // GetStatusMetadata returns metadata for object status -func (cr *VMAnomalyStatus) GetStatusMetadata() *vmv1beta1.StatusMetadata { - return &cr.StatusMetadata +func (cr *VMAnomaly) GetStatusMetadata() *vmv1beta1.StatusMetadata { + return &cr.Status.StatusMetadata } // VMAnomaly is the Schema for the vmanomalies API. diff --git a/api/operator/v1/vtcluster_types.go b/api/operator/v1/vtcluster_types.go index 4ecb655a9..9c5524e7c 100644 --- a/api/operator/v1/vtcluster_types.go +++ b/api/operator/v1/vtcluster_types.go @@ -205,8 +205,8 @@ type VTClusterStatus struct { } // GetStatusMetadata returns metadata for object status -func (cr *VTClusterStatus) GetStatusMetadata() *vmv1beta1.StatusMetadata { - return &cr.StatusMetadata +func (cr *VTCluster) GetStatusMetadata() *vmv1beta1.StatusMetadata { + return &cr.Status.StatusMetadata } // VTInsert defines vtinsert component configuration at victoria-traces cluster diff --git a/api/operator/v1/vtsingle_types.go b/api/operator/v1/vtsingle_types.go index 5c0ec6958..69a4dd093 100644 --- a/api/operator/v1/vtsingle_types.go +++ b/api/operator/v1/vtsingle_types.go @@ -105,8 +105,8 @@ type VTSingleStatus struct { } // GetStatusMetadata returns metadata for object status -func (cr *VTSingleStatus) GetStatusMetadata() *vmv1beta1.StatusMetadata { - return &cr.StatusMetadata +func (cr *VTSingle) GetStatusMetadata() *vmv1beta1.StatusMetadata { + return &cr.Status.StatusMetadata } // VTSingle is fast, cost-effective and scalable traces database. diff --git a/api/operator/v1alpha1/vmdistributed_types.go b/api/operator/v1alpha1/vmdistributed_types.go index beec63041..213dd17e2 100644 --- a/api/operator/v1alpha1/vmdistributed_types.go +++ b/api/operator/v1alpha1/vmdistributed_types.go @@ -397,8 +397,8 @@ func (cr *VMDistributed) DefaultStatusFields(vs *VMDistributedStatus) { } // GetStatusMetadata returns metadata for object status -func (cr *VMDistributedStatus) GetStatusMetadata() *vmv1beta1.StatusMetadata { - return &cr.StatusMetadata +func (cr *VMDistributed) GetStatusMetadata() *vmv1beta1.StatusMetadata { + return &cr.Status.StatusMetadata } // LastAppliedSpecAsPatch return last applied cluster spec as patch annotation diff --git a/api/operator/v1beta1/vlogs_types.go b/api/operator/v1beta1/vlogs_types.go index 53cdc4789..9ac51935c 100644 --- a/api/operator/v1beta1/vlogs_types.go +++ b/api/operator/v1beta1/vlogs_types.go @@ -97,8 +97,8 @@ type VLogsStatus struct { } // GetStatusMetadata returns metadata for object status -func (cr *VLogsStatus) GetStatusMetadata() *StatusMetadata { - return &cr.StatusMetadata +func (cr *VLogs) GetStatusMetadata() *StatusMetadata { + return &cr.Status.StatusMetadata } // VLogs is fast, cost-effective and scalable logs database. diff --git a/api/operator/v1beta1/vmagent_types.go b/api/operator/v1beta1/vmagent_types.go index cc05a5f29..3bbf9888b 100644 --- a/api/operator/v1beta1/vmagent_types.go +++ b/api/operator/v1beta1/vmagent_types.go @@ -415,8 +415,8 @@ type VMAgentStatus struct { } // GetStatusMetadata returns metadata for object status -func (cr *VMAgentStatus) GetStatusMetadata() *StatusMetadata { - return &cr.StatusMetadata +func (cr *VMAgent) GetStatusMetadata() *StatusMetadata { + return &cr.Status.StatusMetadata } // +genclient diff --git a/api/operator/v1beta1/vmalert_types.go b/api/operator/v1beta1/vmalert_types.go index 0e63b72bb..bddeb117a 100644 --- a/api/operator/v1beta1/vmalert_types.go +++ b/api/operator/v1beta1/vmalert_types.go @@ -279,8 +279,8 @@ type VMAlertStatus struct { } // GetStatusMetadata returns metadata for object status -func (cr *VMAlertStatus) GetStatusMetadata() *StatusMetadata { - return &cr.StatusMetadata +func (cr *VMAlert) GetStatusMetadata() *StatusMetadata { + return &cr.Status.StatusMetadata } // VMAlert executes a list of given alerting or recording rules against configured address. diff --git a/api/operator/v1beta1/vmalertmanager_types.go b/api/operator/v1beta1/vmalertmanager_types.go index 69d69dfe8..2b8f7cd71 100644 --- a/api/operator/v1beta1/vmalertmanager_types.go +++ b/api/operator/v1beta1/vmalertmanager_types.go @@ -285,8 +285,8 @@ type VMAlertmanagerStatus struct { } // GetStatusMetadata returns metadata for object status -func (cr *VMAlertmanagerStatus) GetStatusMetadata() *StatusMetadata { - return &cr.StatusMetadata +func (cr *VMAlertmanager) GetStatusMetadata() *StatusMetadata { + return &cr.Status.StatusMetadata } // GetStatus implements reconcile.ObjectWithDeepCopyAndStatus interface diff --git a/api/operator/v1beta1/vmalertmanagerconfig_types.go b/api/operator/v1beta1/vmalertmanagerconfig_types.go index 091186ea4..09fb05744 100644 --- a/api/operator/v1beta1/vmalertmanagerconfig_types.go +++ b/api/operator/v1beta1/vmalertmanagerconfig_types.go @@ -109,6 +109,14 @@ func (r *VMAlertmanagerConfig) GetStatusMetadata() *StatusMetadata { return &r.Status.StatusMetadata } +// GetStatus implements reconcile.ObjectWithDeepCopyAndStatus interface +func (cr *VMAlertmanagerConfig) GetStatus() *VMAlertmanagerConfigStatus { + return &cr.Status +} + +// DefaultStatusFields implements reconcile.ObjectWithDeepCopyAndStatus interface +func (cr *VMAlertmanagerConfig) DefaultStatusFields(vs *VMAlertmanagerConfigStatus) {} + func (r *VMAlertmanagerConfig) Validate() error { if MustSkipCRValidation(r) { return nil diff --git a/api/operator/v1beta1/vmauth_types.go b/api/operator/v1beta1/vmauth_types.go index 63976ca49..b32748550 100644 --- a/api/operator/v1beta1/vmauth_types.go +++ b/api/operator/v1beta1/vmauth_types.go @@ -544,8 +544,8 @@ type VMAuthStatus struct { } // GetStatusMetadata returns metadata for object status -func (cr *VMAuthStatus) GetStatusMetadata() *StatusMetadata { - return &cr.StatusMetadata +func (cr *VMAuth) GetStatusMetadata() *StatusMetadata { + return &cr.Status.StatusMetadata } // VMAuth is the Schema for the vmauths API diff --git a/api/operator/v1beta1/vmcluster_types.go b/api/operator/v1beta1/vmcluster_types.go index 7bbe83753..3752a2bdf 100644 --- a/api/operator/v1beta1/vmcluster_types.go +++ b/api/operator/v1beta1/vmcluster_types.go @@ -256,8 +256,8 @@ type VMClusterStatus struct { } // GetStatusMetadata returns metadata for object status -func (cr *VMClusterStatus) GetStatusMetadata() *StatusMetadata { - return &cr.StatusMetadata +func (cr *VMCluster) GetStatusMetadata() *StatusMetadata { + return &cr.Status.StatusMetadata } // VMClusterList contains a list of VMCluster diff --git a/api/operator/v1beta1/vmnodescrape_types.go b/api/operator/v1beta1/vmnodescrape_types.go index 342daabf9..b76758ed1 100644 --- a/api/operator/v1beta1/vmnodescrape_types.go +++ b/api/operator/v1beta1/vmnodescrape_types.go @@ -87,6 +87,14 @@ func (cr *VMNodeScrape) GetStatusMetadata() *StatusMetadata { return &cr.Status.StatusMetadata } +// GetStatus implements reconcile.ObjectWithDeepCopyAndStatus interface +func (cr *VMNodeScrape) GetStatus() *ScrapeObjectStatus { + return &cr.Status +} + +// DefaultStatusFields implements reconcile.ObjectWithDeepCopyAndStatus interface +func (cr *VMNodeScrape) DefaultStatusFields(vs *ScrapeObjectStatus) {} + // AsKey returns unique key for object func (cr *VMNodeScrape) AsKey(_ bool) string { return cr.Namespace + "/" + cr.Name diff --git a/api/operator/v1beta1/vmpodscrape_types.go b/api/operator/v1beta1/vmpodscrape_types.go index 82ad7deb6..9903bc2ff 100644 --- a/api/operator/v1beta1/vmpodscrape_types.go +++ b/api/operator/v1beta1/vmpodscrape_types.go @@ -152,6 +152,14 @@ func (cr *VMPodScrape) GetStatusMetadata() *StatusMetadata { return &cr.Status.StatusMetadata } +// GetStatus implements reconcile.ObjectWithDeepCopyAndStatus interface +func (cr *VMPodScrape) GetStatus() *ScrapeObjectStatus { + return &cr.Status +} + +// DefaultStatusFields implements reconcile.ObjectWithDeepCopyAndStatus interface +func (cr *VMPodScrape) DefaultStatusFields(vs *ScrapeObjectStatus) {} + func init() { SchemeBuilder.Register(&VMPodScrape{}, &VMPodScrapeList{}) } diff --git a/api/operator/v1beta1/vmprobe_types.go b/api/operator/v1beta1/vmprobe_types.go index 3b4ae4121..167ce57d7 100644 --- a/api/operator/v1beta1/vmprobe_types.go +++ b/api/operator/v1beta1/vmprobe_types.go @@ -167,6 +167,14 @@ func (cr *VMProbe) GetStatusMetadata() *StatusMetadata { return &cr.Status.StatusMetadata } +// GetStatus implements reconcile.ObjectWithDeepCopyAndStatus interface +func (cr *VMProbe) GetStatus() *ScrapeObjectStatus { + return &cr.Status +} + +// DefaultStatusFields implements reconcile.ObjectWithDeepCopyAndStatus interface +func (cr *VMProbe) DefaultStatusFields(vs *ScrapeObjectStatus) {} + // AsKey returns unique key for object func (cr *VMProbe) AsKey(_ bool) string { return cr.Namespace + "/" + cr.Name diff --git a/api/operator/v1beta1/vmrule_types.go b/api/operator/v1beta1/vmrule_types.go index 5db76932e..4f70f4423 100644 --- a/api/operator/v1beta1/vmrule_types.go +++ b/api/operator/v1beta1/vmrule_types.go @@ -1,6 +1,7 @@ package v1beta1 import ( + "encoding/json" "fmt" "net/url" "strconv" @@ -25,6 +26,8 @@ var initVMAlertTemplatesOnce sync.Once type VMRuleSpec struct { // Groups list of group rules Groups []RuleGroup `json:"groups"` + // ParsingError contents error with context if operator was failed to parse json object from kubernetes api server + ParsingError string `json:"-" yaml:"-"` } // RuleGroup is a list of sequentially evaluated recording and alerting rules. @@ -138,6 +141,14 @@ func (cr *VMRule) GetStatusMetadata() *StatusMetadata { return &cr.Status.StatusMetadata } +// GetStatus implements reconcile.ObjectWithDeepCopyAndStatus interface +func (cr *VMRule) GetStatus() *VMRuleStatus { + return &cr.Status +} + +// DefaultStatusFields implements reconcile.ObjectWithDeepCopyAndStatus interface +func (cr *VMRule) DefaultStatusFields(vs *VMRuleStatus) {} + // AsKey returns unique key for object func (cr *VMRule) AsKey(_ bool) string { return fmt.Sprintf("%s-%s.yaml", cr.Namespace, cr.Name) @@ -230,6 +241,16 @@ type VMRule struct { Status VMRuleStatus `json:"status,omitempty"` } +// UnmarshalJSON implements json.Unmarshaler interface +func (r *VMRule) UnmarshalJSON(src []byte) error { + type rcfg VMRule + if err := json.Unmarshal(src, (*rcfg)(r)); err != nil { + r.Spec.ParsingError = fmt.Sprintf("cannot parse vmrule config: %s, err: %s", string(src), err) + return nil + } + return nil +} + // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // VMRuleList contains a list of VMRule diff --git a/api/operator/v1beta1/vmscrapeconfig_types.go b/api/operator/v1beta1/vmscrapeconfig_types.go index fefa0ae59..983305512 100644 --- a/api/operator/v1beta1/vmscrapeconfig_types.go +++ b/api/operator/v1beta1/vmscrapeconfig_types.go @@ -591,6 +591,14 @@ func (cr *VMScrapeConfig) GetStatusMetadata() *StatusMetadata { return &cr.Status.StatusMetadata } +// GetStatus implements reconcile.ObjectWithDeepCopyAndStatus interface +func (cr *VMScrapeConfig) GetStatus() *ScrapeObjectStatus { + return &cr.Status +} + +// DefaultStatusFields implements reconcile.ObjectWithDeepCopyAndStatus interface +func (cr *VMScrapeConfig) DefaultStatusFields(vs *ScrapeObjectStatus) {} + func init() { SchemeBuilder.Register(&VMScrapeConfig{}, &VMScrapeConfigList{}) } diff --git a/api/operator/v1beta1/vmservicescrape_types.go b/api/operator/v1beta1/vmservicescrape_types.go index d1b983f7d..ecc4ce6e5 100644 --- a/api/operator/v1beta1/vmservicescrape_types.go +++ b/api/operator/v1beta1/vmservicescrape_types.go @@ -174,6 +174,14 @@ func (cr *VMServiceScrape) GetStatusMetadata() *StatusMetadata { return &cr.Status.StatusMetadata } +// GetStatus implements reconcile.ObjectWithDeepCopyAndStatus interface +func (cr *VMServiceScrape) GetStatus() *ScrapeObjectStatus { + return &cr.Status +} + +// DefaultStatusFields implements reconcile.ObjectWithDeepCopyAndStatus interface +func (cr *VMServiceScrape) DefaultStatusFields(vs *ScrapeObjectStatus) {} + func init() { SchemeBuilder.Register(&VMServiceScrape{}, &VMServiceScrapeList{}) } diff --git a/api/operator/v1beta1/vmsingle_types.go b/api/operator/v1beta1/vmsingle_types.go index 98183602f..1e2a1f6fa 100644 --- a/api/operator/v1beta1/vmsingle_types.go +++ b/api/operator/v1beta1/vmsingle_types.go @@ -350,8 +350,8 @@ func (cr *VMSingle) Validate() error { } // GetStatusMetadata returns metadata for object status -func (cr *VMSingleStatus) GetStatusMetadata() *StatusMetadata { - return &cr.StatusMetadata +func (cr *VMSingle) GetStatusMetadata() *StatusMetadata { + return &cr.Status.StatusMetadata } // GetAdditionalService returns AdditionalServiceSpec settings diff --git a/api/operator/v1beta1/vmstaticscrape_types.go b/api/operator/v1beta1/vmstaticscrape_types.go index a2b45439b..7030f349d 100644 --- a/api/operator/v1beta1/vmstaticscrape_types.go +++ b/api/operator/v1beta1/vmstaticscrape_types.go @@ -93,6 +93,14 @@ func (cr *VMStaticScrape) GetStatusMetadata() *StatusMetadata { return &cr.Status.StatusMetadata } +// GetStatus implements reconcile.ObjectWithDeepCopyAndStatus interface +func (cr *VMStaticScrape) GetStatus() *ScrapeObjectStatus { + return &cr.Status +} + +// DefaultStatusFields implements reconcile.ObjectWithDeepCopyAndStatus interface +func (cr *VMStaticScrape) DefaultStatusFields(vs *ScrapeObjectStatus) {} + // AsKey returns unique key for object func (cr *VMStaticScrape) AsKey(_ bool) string { return cr.Namespace + "/" + cr.Name diff --git a/api/operator/v1beta1/vmuser_types.go b/api/operator/v1beta1/vmuser_types.go index cd03da1c3..ba0be19fb 100644 --- a/api/operator/v1beta1/vmuser_types.go +++ b/api/operator/v1beta1/vmuser_types.go @@ -1,6 +1,7 @@ package v1beta1 import ( + "encoding/json" "fmt" "strings" @@ -13,6 +14,8 @@ import ( // VMUserSpec defines the desired state of VMUser type VMUserSpec struct { + // ParsingError contents error with context if operator was failed to parse json object from kubernetes api server + ParsingError string `json:"-" yaml:"-"` // Name of the VMUser object. // +optional Name *string `json:"name,omitempty"` @@ -259,6 +262,16 @@ func (cr *VMUser) SelectorLabels() map[string]string { } } +// UnmarshalJSON implements json.Unmarshaler interface +func (cr *VMUserSpec) UnmarshalJSON(src []byte) error { + type pcr VMUserSpec + if err := json.Unmarshal(src, (*pcr)(cr)); err != nil { + cr.ParsingError = fmt.Sprintf("cannot parse vmuserspec: %s, err: %s", string(src), err) + return nil + } + return nil +} + // FinalLabels returns combination of selector and managed labels func (cr *VMUser) FinalLabels() map[string]string { v := cr.SelectorLabels() @@ -273,6 +286,14 @@ func (cr *VMUser) GetStatusMetadata() *StatusMetadata { return &cr.Status.StatusMetadata } +// GetStatus implements reconcile.ObjectWithDeepCopyAndStatus interface +func (cr *VMUser) GetStatus() *VMUserStatus { + return &cr.Status +} + +// DefaultStatusFields implements reconcile.ObjectWithDeepCopyAndStatus interface +func (cr *VMUser) DefaultStatusFields(vs *VMUserStatus) {} + func (cr *VMUser) AsKey(hide bool) string { var id string hideFn := func(s string) string { diff --git a/internal/controller/operator/controllers.go b/internal/controller/operator/controllers.go index b455c17c7..4cf1c925a 100644 --- a/internal/controller/operator/controllers.go +++ b/internal/controller/operator/controllers.go @@ -148,7 +148,6 @@ func handleReconcileErr[T client.Object, ST reconcile.StatusWithMetadata[STC], S deregisterObjectByCollector(ge.requestObject.Name, ge.requestObject.Namespace, ge.controller) getObjectsErrorsTotal.WithLabelValues(ge.controller, ge.requestObject.String()).Inc() if k8serrors.IsNotFound(err) { - err = nil return originResult, nil } case k8serrors.IsConflict(err): @@ -186,72 +185,6 @@ func handleReconcileErr[T client.Object, ST reconcile.StatusWithMetadata[STC], S logger.WithContext(ctx).Error(err, "failed to create error event at kubernetes API during reconciliation error") } } - - return originResult, err -} - -func handleReconcileErrWithoutStatus( - ctx context.Context, - rclient client.Client, - object client.Object, - originResult ctrl.Result, - err error, -) (ctrl.Result, error) { - if err == nil { - return originResult, nil - } - var ge *getError - var pe *parsingError - switch { - case errors.Is(err, context.Canceled): - contextCancelErrorsTotal.Inc() - if !errors.Is(context.Cause(ctx), ErrShutdown) { - originResult.RequeueAfter = time.Second * 5 - } - return originResult, nil - case errors.As(err, &pe): - parseObjectErrorsTotal.WithLabelValues(pe.controller, "unknown").Inc() - case errors.As(err, &ge): - deregisterObjectByCollector(ge.requestObject.Name, ge.requestObject.Namespace, ge.controller) - getObjectsErrorsTotal.WithLabelValues(ge.controller, ge.requestObject.String()).Inc() - if k8serrors.IsNotFound(err) { - err = nil - return originResult, nil - } - case k8serrors.IsConflict(err): - controller := "unknown" - if object != nil && !reflect.ValueOf(object).IsNil() && object.GetNamespace() != "" { - controller = object.GetObjectKind().GroupVersionKind().GroupKind().Kind - } - conflictErrorsTotal.WithLabelValues(controller, "unknown").Inc() - return ctrl.Result{RequeueAfter: time.Second * 5}, nil - } - if object != nil && !reflect.ValueOf(object).IsNil() && object.GetNamespace() != "" { - errEvent := &corev1.Event{ - ObjectMeta: metav1.ObjectMeta{ - Name: "victoria-metrics-operator-" + uuid.New().String(), - Namespace: object.GetNamespace(), - }, - Type: corev1.EventTypeWarning, - Reason: "ReconciliationError", - Message: err.Error(), - Source: corev1.EventSource{ - Component: "victoria-metrics-operator", - }, - LastTimestamp: metav1.NewTime(time.Now()), - InvolvedObject: corev1.ObjectReference{ - Kind: object.GetObjectKind().GroupVersionKind().Kind, - Namespace: object.GetNamespace(), - Name: object.GetName(), - UID: object.GetUID(), - ResourceVersion: object.GetResourceVersion(), - }, - } - if err := rclient.Create(ctx, errEvent); err != nil { - logger.WithContext(ctx).Error(err, "failed to create error event at kubernetes API during reconciliation error") - } - } - return originResult, err } diff --git a/internal/controller/operator/controllers_test.go b/internal/controller/operator/controllers_test.go index 9746b4b07..ecfa434ef 100644 --- a/internal/controller/operator/controllers_test.go +++ b/internal/controller/operator/controllers_test.go @@ -319,23 +319,27 @@ func TestIsSelectorsMatchesTargetCRD(t *testing.T) { }) } -func TestHandleReconcileErrWithoutStatus(t *testing.T) { +func TestHandleReconcileErr(t *testing.T) { type opts struct { + ctx context.Context err error origin ctrl.Result wantResult ctrl.Result wantErr error } - f := func(ctx context.Context, o opts) { + f := func(o opts) { t.Helper() - got, err := handleReconcileErrWithoutStatus(ctx, nil, nil, o.origin, o.err) + if o.ctx == nil { + o.ctx = context.Background() + } + got, err := handleReconcileErr(o.ctx, nil, (*vmv1beta1.VMCluster)(nil), o.origin, o.err) assert.Equal(t, o.wantErr, err) assert.Equal(t, o.wantResult, got) } // no error - f(context.Background(), opts{ + f(opts{ err: nil, origin: ctrl.Result{RequeueAfter: 10}, wantResult: ctrl.Result{RequeueAfter: 10}, @@ -343,7 +347,7 @@ func TestHandleReconcileErrWithoutStatus(t *testing.T) { }) // context canceled - f(context.Background(), opts{ + f(opts{ err: context.Canceled, origin: ctrl.Result{}, wantResult: ctrl.Result{RequeueAfter: time.Second * 5}, @@ -353,43 +357,11 @@ func TestHandleReconcileErrWithoutStatus(t *testing.T) { // context canceled with ErrShutdown shutdownCtx, shutdownCancel := context.WithCancelCause(context.Background()) shutdownCancel(ErrShutdown) - f(shutdownCtx, opts{ + f(opts{ + ctx: shutdownCtx, err: fmt.Errorf("wrapped: %w", errors.Join(context.Canceled, ErrShutdown)), origin: ctrl.Result{}, wantResult: ctrl.Result{}, wantErr: nil, }) } - -func TestHandleReconcileErr(t *testing.T) { - type opts struct { - err error - origin ctrl.Result - wantResult ctrl.Result - wantErr error - } - - f := func(o opts) { - t.Helper() - got, err := handleReconcileErr(context.Background(), nil, (*vmv1beta1.VMCluster)(nil), o.origin, o.err) - assert.Equal(t, o.wantErr, err) - assert.Equal(t, o.wantResult, got) - } - - // no error - f(opts{ - err: nil, - origin: ctrl.Result{RequeueAfter: 10}, - wantResult: ctrl.Result{RequeueAfter: 10}, - wantErr: nil, - }) - - // context canceled - f(opts{ - err: context.Canceled, - origin: ctrl.Result{}, - wantResult: ctrl.Result{RequeueAfter: time.Second * 5}, - wantErr: nil, - }) - -} diff --git a/internal/controller/operator/factory/reconcile/reconcile.go b/internal/controller/operator/factory/reconcile/reconcile.go index 1f0dccfb4..9991bf314 100644 --- a/internal/controller/operator/factory/reconcile/reconcile.go +++ b/internal/controller/operator/factory/reconcile/reconcile.go @@ -156,7 +156,7 @@ func waitForStatus[T client.Object, ST StatusWithMetadata[STC], STC any]( interval time.Duration, status vmv1beta1.UpdateStatus, ) error { - lastStatus := obj.GetStatus().GetStatusMetadata() + lastStatus := obj.GetStatusMetadata() nsn := types.NamespacedName{Name: obj.GetName(), Namespace: obj.GetNamespace()} err := wait.PollUntilContextCancel(ctx, interval, false, func(ctx context.Context) (done bool, err error) { if err = rclient.Get(ctx, nsn, obj); err != nil { @@ -166,7 +166,7 @@ func waitForStatus[T client.Object, ST StatusWithMetadata[STC], STC any]( err = fmt.Errorf("unexpected error during attempt to get %T=%s: %w", obj, nsn.String(), err) return } - lastStatus = obj.GetStatus().GetStatusMetadata() + lastStatus = obj.GetStatusMetadata() return lastStatus != nil && obj.GetGeneration() == lastStatus.ObservedGeneration && lastStatus.UpdateStatus == status, nil }) if err != nil { diff --git a/internal/controller/operator/factory/reconcile/status.go b/internal/controller/operator/factory/reconcile/status.go index 9c8ed0866..df2670ea7 100644 --- a/internal/controller/operator/factory/reconcile/status.go +++ b/internal/controller/operator/factory/reconcile/status.go @@ -175,20 +175,20 @@ type ObjectWithDeepCopyAndStatus[T client.Object, ST StatusWithMetadata[STC], ST client.Object DeepCopy() T GetStatus() ST + GetStatusMetadata() *vmv1beta1.StatusMetadata DefaultStatusFields(ST) } // StatusWithMetadata defines type StatusWithMetadata[T any] interface { DeepCopy() T - GetStatusMetadata() *vmv1beta1.StatusMetadata } // UpdateStatus reconcile provided object status with given actualStatus status func UpdateObjectStatus[T client.Object, ST StatusWithMetadata[STC], STC any](ctx context.Context, rclient client.Client, object ObjectWithDeepCopyAndStatus[T, ST, STC], actualStatus vmv1beta1.UpdateStatus, maybeErr error) error { currentStatus := object.GetStatus() prevStatus := currentStatus.DeepCopy() - currMeta := currentStatus.GetStatusMetadata() + currMeta := object.GetStatusMetadata() newUpdateStatus := actualStatus switch actualStatus { diff --git a/internal/controller/operator/vlagent_controller.go b/internal/controller/operator/vlagent_controller.go index 66ec3f2e4..43c92d87a 100644 --- a/internal/controller/operator/vlagent_controller.go +++ b/internal/controller/operator/vlagent_controller.go @@ -63,35 +63,35 @@ func (r *VLAgentReconciler) Init(rclient client.Client, l logr.Logger, sc *runti func (r *VLAgentReconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ctrl.Result, err error) { l := r.Log.WithValues("vlagent", req.Name, "namespace", req.Namespace) ctx = logger.AddToContext(ctx, l) - instance := &vmv1.VLAgent{} + var instance vmv1.VLAgent defer func() { - result, err = handleReconcileErr(ctx, r.Client, instance, result, err) + result, err = handleReconcileErr(ctx, r.Client, &instance, result, err) }() // Fetch the VLAgent instance - if err := r.Get(ctx, req.NamespacedName, instance); err != nil { - return result, &getError{origin: err, controller: "vlagent", requestObject: req} + if err = r.Get(ctx, req.NamespacedName, &instance); err != nil { + err = &getError{origin: err, controller: "vlagent", requestObject: req} + return } - RegisterObjectStat(instance, "vlagent") + RegisterObjectStat(&instance, "vlagent") if !instance.DeletionTimestamp.IsZero() { - if err := finalize.OnVLAgentDelete(ctx, r.Client, instance); err != nil { - return result, err - } + err = finalize.OnVLAgentDelete(ctx, r.Client, &instance) return } if instance.Spec.ParsingError != "" { - return result, &parsingError{instance.Spec.ParsingError, "vlagent"} + err = &parsingError{instance.Spec.ParsingError, "vlagent"} + return } - if err := finalize.AddFinalizer(ctx, r.Client, instance); err != nil { - return result, err + if err = finalize.AddFinalizer(ctx, r.Client, &instance); err != nil { + return } - r.Client.Scheme().Default(instance) + r.Client.Scheme().Default(&instance) result, err = reconcileAndTrackStatus(ctx, r.Client, instance.DeepCopy(), func() (ctrl.Result, error) { - if err := vlagent.CreateOrUpdate(ctx, instance, r); err != nil { + if err := vlagent.CreateOrUpdate(ctx, &instance, r); err != nil { return result, err } return result, nil diff --git a/internal/controller/operator/vlcluster_controller.go b/internal/controller/operator/vlcluster_controller.go index c3f98a051..502838077 100644 --- a/internal/controller/operator/vlcluster_controller.go +++ b/internal/controller/operator/vlcluster_controller.go @@ -58,33 +58,33 @@ func (r *VLClusterReconciler) Init(rclient client.Client, l logr.Logger, sc *run func (r *VLClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ctrl.Result, err error) { l := r.Log.WithValues("vlcluster", req.Name, "namespace", req.Namespace) ctx = logger.AddToContext(ctx, l) - instance := &vmv1.VLCluster{} + var instance vmv1.VLCluster defer func() { - result, err = handleReconcileErr(ctx, r.Client, instance, result, err) + result, err = handleReconcileErr(ctx, r.Client, &instance, result, err) }() - if err := r.Get(ctx, req.NamespacedName, instance); err != nil { - return result, &getError{err, "vlcluster", req} + if err = r.Get(ctx, req.NamespacedName, &instance); err != nil { + err = &getError{err, "vlcluster", req} + return } - RegisterObjectStat(instance, "vlcluster") + RegisterObjectStat(&instance, "vlcluster") if !instance.DeletionTimestamp.IsZero() { - if err := finalize.OnClusterDelete(ctx, r.Client, instance); err != nil { - return result, err - } + err = finalize.OnClusterDelete(ctx, r.Client, &instance) return } if instance.Spec.ParsingError != "" { - return result, &parsingError{instance.Spec.ParsingError, "vlcluster"} + err = &parsingError{instance.Spec.ParsingError, "vlcluster"} + return } - if err := finalize.AddFinalizer(ctx, r.Client, instance); err != nil { - return result, err + if err = finalize.AddFinalizer(ctx, r.Client, &instance); err != nil { + return } - r.Client.Scheme().Default(instance) + r.Client.Scheme().Default(&instance) result, err = reconcileAndTrackStatus(ctx, r.Client, instance.DeepCopy(), func() (ctrl.Result, error) { - if err := vlcluster.CreateOrUpdate(ctx, r, instance); err != nil { + if err := vlcluster.CreateOrUpdate(ctx, r, &instance); err != nil { return result, fmt.Errorf("failed create or update vlcluster: %w", err) } return result, nil diff --git a/internal/controller/operator/vlogs_controller.go b/internal/controller/operator/vlogs_controller.go index 5db111e8e..6be8408a1 100644 --- a/internal/controller/operator/vlogs_controller.go +++ b/internal/controller/operator/vlogs_controller.go @@ -64,25 +64,25 @@ func (r *VLogsReconciler) Scheme() *runtime.Scheme { func (r *VLogsReconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ctrl.Result, err error) { l := r.Log.WithValues("vlogs", req.Name, "namespace", req.Namespace) ctx = logger.AddToContext(ctx, l) - instance := &vmv1beta1.VLogs{} + var instance vmv1beta1.VLogs defer func() { - result, err = handleReconcileErr(ctx, r.Client, instance, result, err) + result, err = handleReconcileErr(ctx, r.Client, &instance, result, err) }() - if err := r.Get(ctx, req.NamespacedName, instance); err != nil { - return result, &getError{err, "vlogs", req} + if err = r.Get(ctx, req.NamespacedName, &instance); err != nil { + err = &getError{err, "vlogs", req} + return } - RegisterObjectStat(instance, "vlogs") + RegisterObjectStat(&instance, "vlogs") if !instance.DeletionTimestamp.IsZero() { - if err := finalize.OnVLogsDelete(ctx, r.Client, instance); err != nil { - return result, err - } + err = finalize.OnVLogsDelete(ctx, r.Client, &instance) return } if instance.Spec.ParsingError != "" { - return result, &parsingError{instance.Spec.ParsingError, "vlogs"} + err = &parsingError{instance.Spec.ParsingError, "vlogs"} + return } l.Info("VLogs CustomResource transited into read-only state. Please migrate to the VLSingle.") return diff --git a/internal/controller/operator/vlsingle_controller.go b/internal/controller/operator/vlsingle_controller.go index 085ad5d2f..b2dc2841c 100644 --- a/internal/controller/operator/vlsingle_controller.go +++ b/internal/controller/operator/vlsingle_controller.go @@ -59,33 +59,33 @@ func (r *VLSingleReconciler) Init(rclient client.Client, l logr.Logger, sc *runt func (r *VLSingleReconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ctrl.Result, err error) { l := r.Log.WithValues("vlsingle", req.Name, "namespace", req.Namespace) ctx = logger.AddToContext(ctx, l) - instance := &vmv1.VLSingle{} + var instance vmv1.VLSingle defer func() { - result, err = handleReconcileErr(ctx, r.Client, instance, result, err) + result, err = handleReconcileErr(ctx, r.Client, &instance, result, err) }() - if err := r.Get(ctx, req.NamespacedName, instance); err != nil { - return result, &getError{err, "vlsingle", req} + if err = r.Get(ctx, req.NamespacedName, &instance); err != nil { + err = &getError{err, "vlsingle", req} + return } - RegisterObjectStat(instance, "vlsingle") + RegisterObjectStat(&instance, "vlsingle") if !instance.DeletionTimestamp.IsZero() { - if err := finalize.OnVLSingleDelete(ctx, r.Client, instance); err != nil { - return result, err - } + err = finalize.OnVLSingleDelete(ctx, r.Client, &instance) return } if instance.Spec.ParsingError != "" { - return result, &parsingError{instance.Spec.ParsingError, "vlsingle"} + err = &parsingError{instance.Spec.ParsingError, "vlsingle"} + return } - if err := finalize.AddFinalizer(ctx, r.Client, instance); err != nil { - return result, err + if err = finalize.AddFinalizer(ctx, r.Client, &instance); err != nil { + return } - r.Client.Scheme().Default(instance) + r.Client.Scheme().Default(&instance) result, err = reconcileAndTrackStatus(ctx, r.Client, instance.DeepCopy(), func() (ctrl.Result, error) { - if err := vlsingle.CreateOrUpdate(ctx, r, instance); err != nil { + if err := vlsingle.CreateOrUpdate(ctx, r, &instance); err != nil { return result, fmt.Errorf("failed create or update vlsingle: %w", err) } return result, nil diff --git a/internal/controller/operator/vmagent_controller.go b/internal/controller/operator/vmagent_controller.go index 1b375dccb..702571588 100644 --- a/internal/controller/operator/vmagent_controller.go +++ b/internal/controller/operator/vmagent_controller.go @@ -83,14 +83,15 @@ func (r *VMAgentReconciler) Init(rclient client.Client, l logr.Logger, sc *runti func (r *VMAgentReconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ctrl.Result, err error) { l := r.Log.WithValues("vmagent", req.Name, "namespace", req.Namespace) ctx = logger.AddToContext(ctx, l) - instance := &vmv1beta1.VMAgent{} - + var instance vmv1beta1.VMAgent defer func() { - result, err = handleReconcileErr(ctx, r.Client, instance, result, err) + result, err = handleReconcileErr(ctx, r.Client, &instance, result, err) }() + // Fetch the VMAgent instance - if err := r.Get(ctx, req.NamespacedName, instance); err != nil { - return result, &getError{origin: err, controller: "vmagent", requestObject: req} + if err = r.Get(ctx, req.NamespacedName, &instance); err != nil { + err = &getError{origin: err, controller: "vmagent", requestObject: req} + return } if !instance.IsUnmanaged(nil) { @@ -98,25 +99,24 @@ func (r *VMAgentReconciler) Reconcile(ctx context.Context, req ctrl.Request) (re defer agentSync.RUnlock() } - RegisterObjectStat(instance, "vmagent") + RegisterObjectStat(&instance, "vmagent") if !instance.DeletionTimestamp.IsZero() { - if err := finalize.OnVMAgentDelete(ctx, r.Client, instance); err != nil { - return result, err - } + err = finalize.OnVMAgentDelete(ctx, r.Client, &instance) return } if instance.Spec.ParsingError != "" { - return result, &parsingError{instance.Spec.ParsingError, "vmagent"} + err = &parsingError{instance.Spec.ParsingError, "vmagent"} + return } - if err := finalize.AddFinalizer(ctx, r.Client, instance); err != nil { - return result, err + if err = finalize.AddFinalizer(ctx, r.Client, &instance); err != nil { + return } - r.Client.Scheme().Default(instance) + r.Client.Scheme().Default(&instance) result, err = reconcileAndTrackStatus(ctx, r.Client, instance.DeepCopy(), func() (ctrl.Result, error) { - if err := vmagent.CreateOrUpdate(ctx, instance, r); err != nil { + if err := vmagent.CreateOrUpdate(ctx, &instance, r); err != nil { return result, err } return result, nil @@ -151,17 +151,18 @@ func (*VMAgentReconciler) IsDisabled(_ *config.BaseOperatorConf, _ sets.Set[stri return false } -func collectVMAgentScrapes(l logr.Logger, ctx context.Context, rclient client.Client, watchNamespaces []string, instance client.Object) error { +func collectVMAgentScrapes(l logr.Logger, ctx context.Context, rclient client.Client, watchNamespaces []string, instance client.Object) (err error) { if build.IsControllerDisabled("VMAgent") && agentReconcileLimit.MustThrottleReconcile() { return nil } agentSync.Lock() defer agentSync.Unlock() var objects vmv1beta1.VMAgentList - if err := k8stools.ListObjectsByNamespace(ctx, rclient, watchNamespaces, func(dst *vmv1beta1.VMAgentList) { + if err = k8stools.ListObjectsByNamespace(ctx, rclient, watchNamespaces, func(dst *vmv1beta1.VMAgentList) { objects.Items = append(objects.Items, dst.Items...) }); err != nil { - return fmt.Errorf("cannot list VMAgents for %T: %w", instance, err) + err = fmt.Errorf("cannot list VMAgents for %T: %w", instance, err) + return } for i := range objects.Items { item := &objects.Items[i] @@ -191,9 +192,10 @@ func collectVMAgentScrapes(l logr.Logger, ctx context.Context, rclient client.Cl } } - if err := vmagent.CreateOrUpdateScrapeConfig(ctx, rclient, item, instance); err != nil { - continue + if configErr := vmagent.CreateOrUpdateScrapeConfig(ctx, rclient, item, instance); configErr != nil { + l.Error(configErr, "cannot update VMAgent scrape configuration") + err = configErr } } - return nil + return } diff --git a/internal/controller/operator/vmalert_controller.go b/internal/controller/operator/vmalert_controller.go index bdc01a3f8..e3117d75a 100644 --- a/internal/controller/operator/vmalert_controller.go +++ b/internal/controller/operator/vmalert_controller.go @@ -69,14 +69,15 @@ func (r *VMAlertReconciler) Scheme() *runtime.Scheme { func (r *VMAlertReconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ctrl.Result, err error) { l := r.Log.WithValues("vmalert", req.Name, "namespace", req.Namespace) ctx = logger.AddToContext(ctx, l) - instance := &vmv1beta1.VMAlert{} + var instance vmv1beta1.VMAlert defer func() { - result, err = handleReconcileErr(ctx, r.Client, instance, result, err) + result, err = handleReconcileErr(ctx, r.Client, &instance, result, err) }() - if err := r.Get(ctx, req.NamespacedName, instance); err != nil { - return result, &getError{err, "vmalert", req} + if err = r.Get(ctx, req.NamespacedName, &instance); err != nil { + err = &getError{err, "vmalert", req} + return } if !instance.IsUnmanaged() { @@ -84,29 +85,28 @@ func (r *VMAlertReconciler) Reconcile(ctx context.Context, req ctrl.Request) (re defer alertSync.RUnlock() } - RegisterObjectStat(instance, "vmalert") + RegisterObjectStat(&instance, "vmalert") if !instance.DeletionTimestamp.IsZero() { - if err := finalize.OnVMAlertDelete(ctx, r.Client, instance); err != nil { - return result, err - } - return result, nil + err = finalize.OnVMAlertDelete(ctx, r.Client, &instance) + return } if instance.Spec.ParsingError != "" { - return result, &parsingError{instance.Spec.ParsingError, "vmalert"} + err = &parsingError{instance.Spec.ParsingError, "vmalert"} + return } - if err := finalize.AddFinalizer(ctx, r.Client, instance); err != nil { - return result, err + if err = finalize.AddFinalizer(ctx, r.Client, &instance); err != nil { + return } - r.Client.Scheme().Default(instance) + r.Client.Scheme().Default(&instance) result, err = reconcileAndTrackStatus(ctx, r.Client, instance.DeepCopy(), func() (ctrl.Result, error) { - maps, err := vmalert.CreateOrUpdateRuleConfigMaps(ctx, r, instance, nil) + maps, err := vmalert.CreateOrUpdateRuleConfigMaps(ctx, r, &instance, nil) if err != nil { return result, err } - if err := vmalert.CreateOrUpdate(ctx, instance, r, maps); err != nil { + if err := vmalert.CreateOrUpdate(ctx, &instance, r, maps); err != nil { return result, err } return result, nil diff --git a/internal/controller/operator/vmalertmanager_controller.go b/internal/controller/operator/vmalertmanager_controller.go index 8819bc067..ae8595220 100644 --- a/internal/controller/operator/vmalertmanager_controller.go +++ b/internal/controller/operator/vmalertmanager_controller.go @@ -72,13 +72,14 @@ func (r *VMAlertmanagerReconciler) Scheme() *runtime.Scheme { func (r *VMAlertmanagerReconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ctrl.Result, err error) { l := r.Log.WithValues("vmalertmanager", req.Name, "namespace", req.Namespace) ctx = logger.AddToContext(ctx, l) - instance := &vmv1beta1.VMAlertmanager{} + var instance vmv1beta1.VMAlertmanager defer func() { - result, err = handleReconcileErr(ctx, r.Client, instance, result, err) + result, err = handleReconcileErr(ctx, r.Client, &instance, result, err) }() - if err := r.Get(ctx, req.NamespacedName, instance); err != nil { - return result, &getError{err, "vmalertmanager", req} + if err = r.Get(ctx, req.NamespacedName, &instance); err != nil { + err = &getError{err, "vmalertmanager", req} + return } if !instance.IsUnmanaged() { @@ -86,27 +87,26 @@ func (r *VMAlertmanagerReconciler) Reconcile(ctx context.Context, req ctrl.Reque defer alertmanagerSync.RUnlock() } - RegisterObjectStat(instance, "vmalertmanager") + RegisterObjectStat(&instance, "vmalertmanager") if !instance.DeletionTimestamp.IsZero() { - if err := finalize.OnVMAlertManagerDelete(ctx, r.Client, instance); err != nil { - return result, err - } + err = finalize.OnVMAlertManagerDelete(ctx, r.Client, &instance) return } if instance.Spec.ParsingError != "" { - return result, &parsingError{instance.Spec.ParsingError, "vmalertmanager"} + err = &parsingError{instance.Spec.ParsingError, "vmalertmanager"} + return } - if err := finalize.AddFinalizer(ctx, r.Client, instance); err != nil { - return result, err + if err = finalize.AddFinalizer(ctx, r.Client, &instance); err != nil { + return } - r.Client.Scheme().Default(instance) + r.Client.Scheme().Default(&instance) result, err = reconcileAndTrackStatus(ctx, r.Client, instance.DeepCopy(), func() (ctrl.Result, error) { - if err := vmalertmanager.CreateOrUpdateConfig(ctx, r.Client, instance, nil); err != nil { + if err := vmalertmanager.CreateOrUpdateConfig(ctx, r.Client, &instance, nil); err != nil { return result, err } - if err := vmalertmanager.CreateOrUpdateAlertManager(ctx, instance, r); err != nil { + if err := vmalertmanager.CreateOrUpdateAlertManager(ctx, &instance, r); err != nil { return result, err } return result, nil diff --git a/internal/controller/operator/vmalertmanagerconfig_controller.go b/internal/controller/operator/vmalertmanagerconfig_controller.go index 939451881..4b03558bf 100644 --- a/internal/controller/operator/vmalertmanagerconfig_controller.go +++ b/internal/controller/operator/vmalertmanagerconfig_controller.go @@ -58,19 +58,23 @@ func (r *VMAlertmanagerConfigReconciler) Scheme() *runtime.Scheme { // Reconcile implements interface // +kubebuilder:rbac:groups=operator.victoriametrics.com,resources=vmalertmanagerconfigs,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=operator.victoriametrics.com,resources=vmalertmanagerconfigs/status,verbs=get;update;patch -func (r *VMAlertmanagerConfigReconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ctrl.Result, resultErr error) { +func (r *VMAlertmanagerConfigReconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ctrl.Result, err error) { l := r.Log.WithValues("vmalertmanagerconfig", req.Name, "namespace", req.Namespace) var instance vmv1beta1.VMAlertmanagerConfig defer func() { - result, resultErr = handleReconcileErrWithoutStatus(ctx, r.Client, &instance, result, resultErr) + result, err = handleReconcileErr(ctx, r.Client, &instance, result, err) }() - if err := r.Get(ctx, req.NamespacedName, &instance); err != nil { - return result, &getError{err, "vmalertmanagerconfig", req} + if err = r.Get(ctx, req.NamespacedName, &instance); err != nil { + err = &getError{err, "vmalertmanagerconfig", req} + return } RegisterObjectStat(&instance, "vmalertmanagerconfig") - + if instance.Spec.ParsingError != "" { + err = &parsingError{instance.Spec.ParsingError, "vmalertmanagerconfig"} + return + } if alertmanagerReconcileLimit.MustThrottleReconcile() { return } @@ -78,10 +82,11 @@ func (r *VMAlertmanagerConfigReconciler) Reconcile(ctx context.Context, req ctrl alertmanagerSync.Lock() defer alertmanagerSync.Unlock() var objects vmv1beta1.VMAlertmanagerList - if err := k8stools.ListObjectsByNamespace(ctx, r.Client, r.BaseConf.WatchNamespaces, func(dst *vmv1beta1.VMAlertmanagerList) { + if err = k8stools.ListObjectsByNamespace(ctx, r.Client, r.BaseConf.WatchNamespaces, func(dst *vmv1beta1.VMAlertmanagerList) { objects.Items = append(objects.Items, dst.Items...) }); err != nil { - return result, fmt.Errorf("cannot list vmalertmanagers for vmalertmanagerconfig: %w", err) + err = fmt.Errorf("cannot list vmalertmanagers for vmalertmanagerconfig: %w", err) + return } for i := range objects.Items { @@ -111,8 +116,9 @@ func (r *VMAlertmanagerConfigReconciler) Reconcile(ctx context.Context, req ctrl continue } } - if err := vmalertmanager.CreateOrUpdateConfig(ctx, r.Client, item, &instance); err != nil { - continue + if configErr := vmalertmanager.CreateOrUpdateConfig(ctx, r.Client, item, &instance); configErr != nil { + l.Error(configErr, "cannot update alertmanager config") + err = configErr } } return diff --git a/internal/controller/operator/vmanomaly_controller.go b/internal/controller/operator/vmanomaly_controller.go index 4f518cfff..7732c59b6 100644 --- a/internal/controller/operator/vmanomaly_controller.go +++ b/internal/controller/operator/vmanomaly_controller.go @@ -63,35 +63,35 @@ func (r *VMAnomalyReconciler) Init(rclient client.Client, l logr.Logger, sc *run func (r *VMAnomalyReconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ctrl.Result, err error) { l := r.Log.WithValues("vmanomaly", req.Name, "namespace", req.Namespace) ctx = logger.AddToContext(ctx, l) - instance := &vmv1.VMAnomaly{} + var instance vmv1.VMAnomaly defer func() { - result, err = handleReconcileErr(ctx, r.Client, instance, result, err) + result, err = handleReconcileErr(ctx, r.Client, &instance, result, err) }() // Fetch the VMAnomaly instance - if err := r.Get(ctx, req.NamespacedName, instance); err != nil { - return result, &getError{origin: err, controller: "vmanomaly", requestObject: req} + if err = r.Get(ctx, req.NamespacedName, &instance); err != nil { + err = &getError{origin: err, controller: "vmanomaly", requestObject: req} + return } - RegisterObjectStat(instance, "vmanomaly") + RegisterObjectStat(&instance, "vmanomaly") if !instance.DeletionTimestamp.IsZero() { - if err := finalize.OnVMAnomalyDelete(ctx, r.Client, instance); err != nil { - return result, err - } + err = finalize.OnVMAnomalyDelete(ctx, r.Client, &instance) return } if instance.Spec.ParsingError != "" { - return result, &parsingError{instance.Spec.ParsingError, "vmanomaly"} + err = &parsingError{instance.Spec.ParsingError, "vmanomaly"} + return } - if err := finalize.AddFinalizer(ctx, r.Client, instance); err != nil { - return result, err + if err = finalize.AddFinalizer(ctx, r.Client, &instance); err != nil { + return } - r.Client.Scheme().Default(instance) + r.Client.Scheme().Default(&instance) result, err = reconcileAndTrackStatus(ctx, r.Client, instance.DeepCopy(), func() (ctrl.Result, error) { - if err := vmanomaly.CreateOrUpdate(ctx, instance, r); err != nil { + if err := vmanomaly.CreateOrUpdate(ctx, &instance, r); err != nil { return result, err } return result, nil diff --git a/internal/controller/operator/vmauth_controller.go b/internal/controller/operator/vmauth_controller.go index eb1d0e62b..c8f47b891 100644 --- a/internal/controller/operator/vmauth_controller.go +++ b/internal/controller/operator/vmauth_controller.go @@ -69,13 +69,13 @@ func (r *VMAuthReconciler) Scheme() *runtime.Scheme { func (r *VMAuthReconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ctrl.Result, err error) { l := r.Log.WithValues("vmauth", req.Name, "namespace", req.Namespace) ctx = logger.AddToContext(ctx, l) - instance := &vmv1beta1.VMAuth{} + var instance vmv1beta1.VMAuth defer func() { - result, err = handleReconcileErr(ctx, r.Client, instance, result, err) + result, err = handleReconcileErr(ctx, r.Client, &instance, result, err) }() - if err := r.Get(ctx, req.NamespacedName, instance); err != nil { + if err := r.Get(ctx, req.NamespacedName, &instance); err != nil { return result, &getError{err, "vmauth", req} } @@ -84,24 +84,25 @@ func (r *VMAuthReconciler) Reconcile(ctx context.Context, req ctrl.Request) (res defer authSync.RUnlock() } - RegisterObjectStat(instance, "vmauth") + RegisterObjectStat(&instance, "vmauth") if !instance.DeletionTimestamp.IsZero() { - if err := finalize.OnVMAuthDelete(ctx, r, instance); err != nil { - return result, fmt.Errorf("cannot remove finalizer from vmauth: %w", err) + if err = finalize.OnVMAuthDelete(ctx, r, &instance); err != nil { + err = fmt.Errorf("cannot remove finalizer from vmauth: %w", err) } - return result, nil + return } if instance.Spec.ParsingError != "" { - return result, &parsingError{instance.Spec.ParsingError, "vmauth"} + err = &parsingError{instance.Spec.ParsingError, "vmauth"} + return } - if err := finalize.AddFinalizer(ctx, r.Client, instance); err != nil { - return result, err + if err = finalize.AddFinalizer(ctx, r.Client, &instance); err != nil { + return } - r.Client.Scheme().Default(instance) + r.Client.Scheme().Default(&instance) result, err = reconcileAndTrackStatus(ctx, r.Client, instance.DeepCopy(), func() (ctrl.Result, error) { - if err := vmauth.CreateOrUpdate(ctx, instance, r); err != nil { + if err := vmauth.CreateOrUpdate(ctx, &instance, r); err != nil { return result, fmt.Errorf("cannot create or update vmauth deploy: %w", err) } return result, nil diff --git a/internal/controller/operator/vmcluster_controller.go b/internal/controller/operator/vmcluster_controller.go index fc95a646c..2369c7414 100644 --- a/internal/controller/operator/vmcluster_controller.go +++ b/internal/controller/operator/vmcluster_controller.go @@ -47,34 +47,34 @@ func (r *VMClusterReconciler) Scheme() *runtime.Scheme { func (r *VMClusterReconciler) Reconcile(ctx context.Context, request ctrl.Request) (result ctrl.Result, err error) { l := r.Log.WithValues("vmcluster", request.Name, "namespace", request.Namespace) ctx = logger.AddToContext(ctx, l) - instance := &vmv1beta1.VMCluster{} + var instance vmv1beta1.VMCluster defer func() { - result, err = handleReconcileErr(ctx, r.Client, instance, result, err) + result, err = handleReconcileErr(ctx, r.Client, &instance, result, err) }() - if err := r.Client.Get(ctx, request.NamespacedName, instance); err != nil { - return result, &getError{err, "vmcluster", request} + if err = r.Client.Get(ctx, request.NamespacedName, &instance); err != nil { + err = &getError{err, "vmcluster", request} + return } - RegisterObjectStat(instance, "vmcluster") + RegisterObjectStat(&instance, "vmcluster") if !instance.DeletionTimestamp.IsZero() { - if err := finalize.OnClusterDelete(ctx, r.Client, instance); err != nil { - return result, err - } - return result, nil + err = finalize.OnClusterDelete(ctx, r.Client, &instance) + return } if instance.Spec.ParsingError != "" { - return result, &parsingError{instance.Spec.ParsingError, "vmcluster"} + err = &parsingError{instance.Spec.ParsingError, "vmcluster"} + return } - if err := finalize.AddFinalizer(ctx, r.Client, instance); err != nil { - return result, err + if err = finalize.AddFinalizer(ctx, r.Client, &instance); err != nil { + return } - r.Client.Scheme().Default(instance) + r.Client.Scheme().Default(&instance) result, err = reconcileAndTrackStatus(ctx, r.Client, instance.DeepCopy(), func() (ctrl.Result, error) { - if err := vmcluster.CreateOrUpdate(ctx, instance, r.Client); err != nil { + if err := vmcluster.CreateOrUpdate(ctx, &instance, r.Client); err != nil { return result, fmt.Errorf("failed create or update vmcluster: %w", err) } return result, nil diff --git a/internal/controller/operator/vmdistributed_controller.go b/internal/controller/operator/vmdistributed_controller.go index d0bbb143a..e3095fd10 100644 --- a/internal/controller/operator/vmdistributed_controller.go +++ b/internal/controller/operator/vmdistributed_controller.go @@ -55,40 +55,42 @@ func (r *VMDistributedReconciler) Init(rclient client.Client, l logr.Logger, sc func (r *VMDistributedReconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ctrl.Result, err error) { l := r.Log.WithValues("vmdistributed", req.Name, "namespace", req.Namespace) ctx = logger.AddToContext(ctx, l) - instance := &vmv1alpha1.VMDistributed{} + var instance vmv1alpha1.VMDistributed // Handle reconcile errors defer func() { - result, err = handleReconcileErr(ctx, r.Client, instance, result, err) + result, err = handleReconcileErr(ctx, r.Client, &instance, result, err) }() // Fetch VMDistributed instance - if err := r.Get(ctx, req.NamespacedName, instance); err != nil { - return result, &getError{err, "vmdistributed", req} + if err = r.Get(ctx, req.NamespacedName, &instance); err != nil { + err = &getError{err, "vmdistributed", req} + return } // Register metrics - RegisterObjectStat(instance, "vmdistributed") + RegisterObjectStat(&instance, "vmdistributed") // Check if the instance is being deleted if !instance.DeletionTimestamp.IsZero() { - if err := finalize.OnVMDistributedDelete(ctx, r, instance); err != nil { - return result, fmt.Errorf("cannot remove finalizer from VMDistributed: %w", err) + if err = finalize.OnVMDistributedDelete(ctx, r, &instance); err != nil { + err = fmt.Errorf("cannot remove finalizer from VMDistributed: %w", err) } - return result, nil + return } // Check parsing error if instance.Spec.ParsingError != "" { - return result, &parsingError{instance.Spec.ParsingError, "VMDistributed"} + err = &parsingError{instance.Spec.ParsingError, "VMDistributed"} + return } // Add finalizer if necessary - if err := finalize.AddFinalizer(ctx, r.Client, instance); err != nil { - return result, err + if err = finalize.AddFinalizer(ctx, r.Client, &instance); err != nil { + return } - r.Client.Scheme().Default(instance) + r.Client.Scheme().Default(&instance) result, err = reconcileAndTrackStatus(ctx, r.Client, instance.DeepCopy(), func() (ctrl.Result, error) { - if err := vmdistributed.CreateOrUpdate(ctx, instance, r); err != nil { + if err := vmdistributed.CreateOrUpdate(ctx, &instance, r); err != nil { return result, fmt.Errorf("VMDistributed %s update failed: %w", instance.Name, err) } diff --git a/internal/controller/operator/vmnodescrape_controller.go b/internal/controller/operator/vmnodescrape_controller.go index c33c7b63d..3798f691b 100644 --- a/internal/controller/operator/vmnodescrape_controller.go +++ b/internal/controller/operator/vmnodescrape_controller.go @@ -58,22 +58,23 @@ func (r *VMNodeScrapeReconciler) Scheme() *runtime.Scheme { // +kubebuilder:rbac:groups=operator.victoriametrics.com,resources=vmnodescrapes/finalizers,verbs=* func (r *VMNodeScrapeReconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ctrl.Result, err error) { l := r.Log.WithValues("vmnodescrape", req.Name, "namespace", req.Namespace) - instance := &vmv1beta1.VMNodeScrape{} + var instance vmv1beta1.VMNodeScrape ctx = logger.AddToContext(ctx, l) defer func() { - result, err = handleReconcileErrWithoutStatus(ctx, r.Client, instance, result, err) + result, err = handleReconcileErr(ctx, r.Client, &instance, result, err) }() // Fetch the VMNodeScrape instance - if err := r.Get(ctx, req.NamespacedName, instance); err != nil { - return result, &getError{err, "vmnodescrape", req} + if err = r.Get(ctx, req.NamespacedName, &instance); err != nil { + err = &getError{err, "vmnodescrape", req} + return } - - RegisterObjectStat(instance, "vmnodescrape") + RegisterObjectStat(&instance, "vmnodescrape") if instance.Spec.ParsingError != "" { - return result, &parsingError{instance.Spec.ParsingError, "vmnodescrape"} + err = &parsingError{instance.Spec.ParsingError, "vmnodescrape"} + return } - if err = collectVMAgentScrapes(l, ctx, r.Client, r.BaseConf.WatchNamespaces, instance); err != nil { + if err = collectVMAgentScrapes(l, ctx, r.Client, r.BaseConf.WatchNamespaces, &instance); err != nil { return } return diff --git a/internal/controller/operator/vmpodscrape_controller.go b/internal/controller/operator/vmpodscrape_controller.go index e6716f8cc..7167e4373 100644 --- a/internal/controller/operator/vmpodscrape_controller.go +++ b/internal/controller/operator/vmpodscrape_controller.go @@ -57,22 +57,23 @@ func (r *VMPodScrapeReconciler) Scheme() *runtime.Scheme { // +kubebuilder:rbac:groups=operator.victoriametrics.com,resources=vmpodscrapes/status,verbs=get;update;patch func (r *VMPodScrapeReconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ctrl.Result, err error) { l := r.Log.WithValues("vmpodscrape", req.Name, "namespace", req.Namespace) - instance := &vmv1beta1.VMPodScrape{} + var instance vmv1beta1.VMPodScrape ctx = logger.AddToContext(ctx, l) defer func() { - result, err = handleReconcileErrWithoutStatus(ctx, r.Client, instance, result, err) + result, err = handleReconcileErr(ctx, r.Client, &instance, result, err) }() // Fetch the VMPodScrape instance - if err := r.Get(ctx, req.NamespacedName, instance); err != nil { - return result, &getError{err, "vmpodscrape", req} + if err = r.Get(ctx, req.NamespacedName, &instance); err != nil { + err = &getError{err, "vmpodscrape", req} + return } - - RegisterObjectStat(instance, "vmpodscrape") + RegisterObjectStat(&instance, "vmpodscrape") if instance.Spec.ParsingError != "" { - return result, &parsingError{instance.Spec.ParsingError, "vmpodscrape"} + err = &parsingError{instance.Spec.ParsingError, "vmpodscrape"} + return } - if err = collectVMAgentScrapes(l, ctx, r.Client, r.BaseConf.WatchNamespaces, instance); err != nil { + if err = collectVMAgentScrapes(l, ctx, r.Client, r.BaseConf.WatchNamespaces, &instance); err != nil { return } return diff --git a/internal/controller/operator/vmprobe_controller.go b/internal/controller/operator/vmprobe_controller.go index 910b793d2..c5c65b165 100644 --- a/internal/controller/operator/vmprobe_controller.go +++ b/internal/controller/operator/vmprobe_controller.go @@ -57,22 +57,23 @@ func (r *VMProbeReconciler) Scheme() *runtime.Scheme { // +kubebuilder:rbac:groups=operator.victoriametrics.com,resources=vmprobes/status,verbs=get;update;patch func (r *VMProbeReconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ctrl.Result, err error) { l := r.Log.WithValues("vmprobe", req.Name, "namespace", req.Namespace) - instance := &vmv1beta1.VMProbe{} + var instance vmv1beta1.VMProbe ctx = logger.AddToContext(ctx, l) defer func() { - result, err = handleReconcileErrWithoutStatus(ctx, r.Client, instance, result, err) + result, err = handleReconcileErr(ctx, r.Client, &instance, result, err) }() // Fetch the VMPodScrape instance - if err := r.Get(ctx, req.NamespacedName, instance); err != nil { - return result, &getError{err, "vmprobescrape", req} + if err = r.Get(ctx, req.NamespacedName, &instance); err != nil { + err = &getError{err, "vmprobescrape", req} + return } - - RegisterObjectStat(instance, "vmprobescrape") + RegisterObjectStat(&instance, "vmprobescrape") if instance.Spec.ParsingError != "" { - return result, &parsingError{instance.Spec.ParsingError, "vmprobescrape"} + err = &parsingError{instance.Spec.ParsingError, "vmprobescrape"} + return } - if err = collectVMAgentScrapes(l, ctx, r.Client, r.BaseConf.WatchNamespaces, instance); err != nil { + if err = collectVMAgentScrapes(l, ctx, r.Client, r.BaseConf.WatchNamespaces, &instance); err != nil { return } return diff --git a/internal/controller/operator/vmrule_controller.go b/internal/controller/operator/vmrule_controller.go index 3ed79b0b9..0bd25c6e5 100644 --- a/internal/controller/operator/vmrule_controller.go +++ b/internal/controller/operator/vmrule_controller.go @@ -60,32 +60,36 @@ func (r *VMRuleReconciler) Scheme() *runtime.Scheme { // +kubebuilder:rbac:groups=operator.victoriametrics.com,resources=vmrules/status,verbs=get;update;patch func (r *VMRuleReconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ctrl.Result, err error) { l := r.Log.WithValues("vmrule", req.Name, "namespace", req.Namespace) - instance := &vmv1beta1.VMRule{} + var instance vmv1beta1.VMRule ctx = logger.AddToContext(ctx, l) defer func() { - result, err = handleReconcileErrWithoutStatus(ctx, r.Client, instance, result, err) + result, err = handleReconcileErr(ctx, r.Client, &instance, result, err) }() // Fetch the VMRule instance - if err := r.Get(ctx, req.NamespacedName, instance); err != nil { - return result, &getError{err, "vmrule", req} + if err = r.Get(ctx, req.NamespacedName, &instance); err != nil { + err = &getError{err, "vmrule", req} + return } - RegisterObjectStat(instance, "vmrule") - + RegisterObjectStat(&instance, "vmrule") + if instance.Spec.ParsingError != "" { + err = &parsingError{instance.Spec.ParsingError, "vmrule"} + return + } if alertReconcileLimit.MustThrottleReconcile() { - // fast path - return ctrl.Result{}, nil + return } alertSync.Lock() defer alertSync.Unlock() var objects vmv1beta1.VMAlertList - if err := k8stools.ListObjectsByNamespace(ctx, r.Client, r.BaseConf.WatchNamespaces, func(dst *vmv1beta1.VMAlertList) { + if err = k8stools.ListObjectsByNamespace(ctx, r.Client, r.BaseConf.WatchNamespaces, func(dst *vmv1beta1.VMAlertList) { objects.Items = append(objects.Items, dst.Items...) }); err != nil { - return result, fmt.Errorf("cannot list vmalerts for vmrule: %w", err) + err = fmt.Errorf("cannot list vmalerts for vmrule: %w", err) + return } for i := range objects.Items { @@ -105,7 +109,7 @@ func (r *VMRuleReconciler) Reconcile(ctx context.Context, req ctrl.Request) (res ObjectSelector: item.Spec.RuleSelector, DefaultNamespace: instance.Namespace, } - match, err := isSelectorsMatchesTargetCRD(ctx, r.Client, instance, item, opts) + match, err := isSelectorsMatchesTargetCRD(ctx, r.Client, &instance, item, opts) if err != nil { l.Error(err, "cannot match vmalert and vmrule") continue @@ -115,9 +119,9 @@ func (r *VMRuleReconciler) Reconcile(ctx context.Context, req ctrl.Request) (res } } - _, err := vmalert.CreateOrUpdateRuleConfigMaps(ctx, r, item, instance) - if err != nil { - return ctrl.Result{}, fmt.Errorf("cannot update rules configmaps: %w", err) + if _, configErr := vmalert.CreateOrUpdateRuleConfigMaps(ctx, r, item, &instance); configErr != nil { + l.Error(configErr, "cannot update rules configmaps") + err = configErr } } return diff --git a/internal/controller/operator/vmscrapeconfig_controller.go b/internal/controller/operator/vmscrapeconfig_controller.go index 1cd9b3d52..290b0e0ed 100644 --- a/internal/controller/operator/vmscrapeconfig_controller.go +++ b/internal/controller/operator/vmscrapeconfig_controller.go @@ -57,22 +57,23 @@ func (r *VMScrapeConfigReconciler) Scheme() *runtime.Scheme { // +kubebuilder:rbac:groups=operator.victoriametrics.com,resources=vmscrapeconfigs/status,verbs=get;update;patch func (r *VMScrapeConfigReconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ctrl.Result, err error) { l := r.Log.WithValues("vmscrapeconfig", req.Name, "namespace", req.Namespace) - instance := &vmv1beta1.VMScrapeConfig{} + var instance vmv1beta1.VMScrapeConfig ctx = logger.AddToContext(ctx, l) defer func() { - result, err = handleReconcileErrWithoutStatus(ctx, r.Client, instance, result, err) + result, err = handleReconcileErr(ctx, r.Client, &instance, result, err) }() // Fetch the VMScrapeConfig instance - if err := r.Get(ctx, req.NamespacedName, instance); err != nil { - return result, &getError{err, "vmscrapeconfig", req} + if err = r.Get(ctx, req.NamespacedName, &instance); err != nil { + err = &getError{err, "vmscrapeconfig", req} + return } - - RegisterObjectStat(instance, "vmscrapeconfig") + RegisterObjectStat(&instance, "vmscrapeconfig") if instance.Spec.ParsingError != "" { - return result, &parsingError{instance.Spec.ParsingError, "vmscrapeconfig"} + err = &parsingError{instance.Spec.ParsingError, "vmscrapeconfig"} + return } - if err = collectVMAgentScrapes(l, ctx, r.Client, r.BaseConf.WatchNamespaces, instance); err != nil { + if err = collectVMAgentScrapes(l, ctx, r.Client, r.BaseConf.WatchNamespaces, &instance); err != nil { return } return diff --git a/internal/controller/operator/vmservicescrape_controller.go b/internal/controller/operator/vmservicescrape_controller.go index 435b6b33a..a8021066d 100644 --- a/internal/controller/operator/vmservicescrape_controller.go +++ b/internal/controller/operator/vmservicescrape_controller.go @@ -57,22 +57,23 @@ func (r *VMServiceScrapeReconciler) Scheme() *runtime.Scheme { // +kubebuilder:rbac:groups=operator.victoriametrics.com,resources=vmservicescrapes/status,verbs=get;update;patch func (r *VMServiceScrapeReconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ctrl.Result, err error) { l := r.Log.WithValues("vmservicescrape", req.Name, "namespace", req.Namespace) - instance := &vmv1beta1.VMServiceScrape{} ctx = logger.AddToContext(ctx, l) + var instance vmv1beta1.VMServiceScrape defer func() { - result, err = handleReconcileErrWithoutStatus(ctx, r.Client, instance, result, err) + result, err = handleReconcileErr(ctx, r.Client, &instance, result, err) }() // Fetch the VMServiceScrape instance - if err := r.Get(ctx, req.NamespacedName, instance); err != nil { - return result, &getError{err, "vmservicescrape", req} + if err = r.Get(ctx, req.NamespacedName, &instance); err != nil { + err = &getError{err, "vmservicescrape", req} + return } - - RegisterObjectStat(instance, "vmservicescrape") + RegisterObjectStat(&instance, "vmservicescrape") if instance.Spec.ParsingError != "" { - return result, &parsingError{instance.Spec.ParsingError, "vmservicescrape"} + err = &parsingError{instance.Spec.ParsingError, "vmservicescrape"} + return } - if err = collectVMAgentScrapes(l, ctx, r.Client, r.BaseConf.WatchNamespaces, instance); err != nil { + if err = collectVMAgentScrapes(l, ctx, r.Client, r.BaseConf.WatchNamespaces, &instance); err != nil { return } return diff --git a/internal/controller/operator/vmsingle_controller.go b/internal/controller/operator/vmsingle_controller.go index 63849582b..84a1b7b75 100644 --- a/internal/controller/operator/vmsingle_controller.go +++ b/internal/controller/operator/vmsingle_controller.go @@ -66,33 +66,33 @@ func (r *VMSingleReconciler) Scheme() *runtime.Scheme { func (r *VMSingleReconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ctrl.Result, err error) { l := r.Log.WithValues("vmsingle", req.Name, "namespace", req.Namespace) ctx = logger.AddToContext(ctx, l) - instance := &vmv1beta1.VMSingle{} + var instance vmv1beta1.VMSingle defer func() { - result, err = handleReconcileErr(ctx, r.Client, instance, result, err) + result, err = handleReconcileErr(ctx, r.Client, &instance, result, err) }() - if err := r.Get(ctx, req.NamespacedName, instance); err != nil { - return result, &getError{err, "vmsingle", req} + if err = r.Get(ctx, req.NamespacedName, &instance); err != nil { + err = &getError{err, "vmsingle", req} + return } - RegisterObjectStat(instance, "vmsingle") + RegisterObjectStat(&instance, "vmsingle") if !instance.DeletionTimestamp.IsZero() { - if err := finalize.OnVMSingleDelete(ctx, r.Client, instance); err != nil { - return result, err - } + err = finalize.OnVMSingleDelete(ctx, r.Client, &instance) return } if instance.Spec.ParsingError != "" { - return result, &parsingError{instance.Spec.ParsingError, "vmsingle"} + err = &parsingError{instance.Spec.ParsingError, "vmsingle"} + return } - if err := finalize.AddFinalizer(ctx, r.Client, instance); err != nil { - return result, err + if err = finalize.AddFinalizer(ctx, r.Client, &instance); err != nil { + return } - r.Client.Scheme().Default(instance) + r.Client.Scheme().Default(&instance) result, err = reconcileAndTrackStatus(ctx, r.Client, instance.DeepCopy(), func() (ctrl.Result, error) { - if err := vmsingle.CreateOrUpdate(ctx, instance, r); err != nil { + if err := vmsingle.CreateOrUpdate(ctx, &instance, r); err != nil { return result, fmt.Errorf("failed create or update vmsingle: %w", err) } return result, nil diff --git a/internal/controller/operator/vmstaticscrape_controller.go b/internal/controller/operator/vmstaticscrape_controller.go index 2c6dc058c..c79ba7d03 100644 --- a/internal/controller/operator/vmstaticscrape_controller.go +++ b/internal/controller/operator/vmstaticscrape_controller.go @@ -40,18 +40,20 @@ func (r *VMStaticScrapeReconciler) Scheme() *runtime.Scheme { // +kubebuilder:rbac:groups=operator.victoriametrics.com,resources=vmstaticscrapes/status,verbs=get;update;patch func (r *VMStaticScrapeReconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ctrl.Result, err error) { l := r.Log.WithValues("vmstaticscrape", req.Name, "namespace", req.Namespace) - instance := &vmv1beta1.VMStaticScrape{} + var instance vmv1beta1.VMStaticScrape defer func() { - result, err = handleReconcileErrWithoutStatus(ctx, r.Client, instance, result, err) + result, err = handleReconcileErr(ctx, r.Client, &instance, result, err) }() - if err := r.Get(ctx, req.NamespacedName, instance); err != nil { - return result, &getError{err, "vmstaticscrape", req} + if err = r.Get(ctx, req.NamespacedName, &instance); err != nil { + err = &getError{err, "vmstaticscrape", req} + return } - RegisterObjectStat(instance, "vmstaticscrape") + RegisterObjectStat(&instance, "vmstaticscrape") if instance.Spec.ParsingError != "" { - return result, &parsingError{instance.Spec.ParsingError, "vmstaticscrape"} + err = &parsingError{instance.Spec.ParsingError, "vmstaticscrape"} + return } - if err = collectVMAgentScrapes(l, ctx, r.Client, r.BaseConf.WatchNamespaces, instance); err != nil { + if err = collectVMAgentScrapes(l, ctx, r.Client, r.BaseConf.WatchNamespaces, &instance); err != nil { return } return diff --git a/internal/controller/operator/vmuser_controller.go b/internal/controller/operator/vmuser_controller.go index 2b9ae4a4b..44965ca72 100644 --- a/internal/controller/operator/vmuser_controller.go +++ b/internal/controller/operator/vmuser_controller.go @@ -65,24 +65,28 @@ func (r *VMUserReconciler) Reconcile(ctx context.Context, req ctrl.Request) (res l := r.Log.WithValues("vmuser", req.Name, "namespace", req.Namespace) var instance vmv1beta1.VMUser defer func() { - result, err = handleReconcileErrWithoutStatus(ctx, r.Client, &instance, result, err) + result, err = handleReconcileErr(ctx, r.Client, &instance, result, err) }() - if err := r.Get(ctx, req.NamespacedName, &instance); err != nil { - return result, &getError{err, "vmuser", req} + if err = r.Get(ctx, req.NamespacedName, &instance); err != nil { + err = &getError{err, "vmuser", req} + return } - RegisterObjectStat(&instance, "vmuser") - if !instance.DeletionTimestamp.IsZero() { // need to remove finalizer and delete related resources. - if err := finalize.OnVMUserDelete(ctx, r, &instance); err != nil { - return result, fmt.Errorf("cannot remove finalizer for vmuser: %w", err) + if err = finalize.OnVMUserDelete(ctx, r, &instance); err != nil { + err = fmt.Errorf("cannot remove finalizer for vmuser: %w", err) } return } + RegisterObjectStat(&instance, "vmuser") + if instance.Spec.ParsingError != "" { + err = &parsingError{instance.Spec.ParsingError, "vmuser"} + return + } - if err := finalize.AddFinalizer(ctx, r.Client, &instance); err != nil { - return result, err + if err = finalize.AddFinalizer(ctx, r.Client, &instance); err != nil { + return } if authReconcileLimit.MustThrottleReconcile() { @@ -92,10 +96,11 @@ func (r *VMUserReconciler) Reconcile(ctx context.Context, req ctrl.Request) (res authSync.Lock() defer authSync.Unlock() var objects vmv1beta1.VMAuthList - if err := k8stools.ListObjectsByNamespace(ctx, r.Client, r.BaseConf.WatchNamespaces, func(dst *vmv1beta1.VMAuthList) { + if err = k8stools.ListObjectsByNamespace(ctx, r.Client, r.BaseConf.WatchNamespaces, func(dst *vmv1beta1.VMAuthList) { objects.Items = append(objects.Items, dst.Items...) }); err != nil { - return result, fmt.Errorf("cannot list vmauths for vmuser: %w", err) + err = fmt.Errorf("cannot list vmauths for vmuser: %w", err) + return } for i := range objects.Items { @@ -125,8 +130,9 @@ func (r *VMUserReconciler) Reconcile(ctx context.Context, req ctrl.Request) (res continue } } - if err := vmauth.CreateOrUpdateConfig(ctx, r, item, &instance); err != nil { - return ctrl.Result{}, fmt.Errorf("cannot create or update vmauth deploy for vmuser: %w", err) + if configErr := vmauth.CreateOrUpdateConfig(ctx, r, item, &instance); configErr != nil { + l.Error(configErr, "cannot create or update vmauth deploy for vmuser") + err = configErr } } return diff --git a/internal/controller/operator/vtcluster_controller.go b/internal/controller/operator/vtcluster_controller.go index ba1e5af9e..5163971a0 100644 --- a/internal/controller/operator/vtcluster_controller.go +++ b/internal/controller/operator/vtcluster_controller.go @@ -58,33 +58,33 @@ func (r *VTClusterReconciler) Init(rclient client.Client, l logr.Logger, sc *run func (r *VTClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ctrl.Result, err error) { l := r.Log.WithValues("vtcluster", req.Name, "namespace", req.Namespace) ctx = logger.AddToContext(ctx, l) - instance := &vmv1.VTCluster{} + var instance vmv1.VTCluster defer func() { - result, err = handleReconcileErr(ctx, r.Client, instance, result, err) + result, err = handleReconcileErr(ctx, r.Client, &instance, result, err) }() - if err := r.Get(ctx, req.NamespacedName, instance); err != nil { - return result, &getError{err, "vtcluster", req} + if err = r.Get(ctx, req.NamespacedName, &instance); err != nil { + err = &getError{err, "vtcluster", req} + return } - RegisterObjectStat(instance, "vtcluster") + RegisterObjectStat(&instance, "vtcluster") if !instance.DeletionTimestamp.IsZero() { - if err := finalize.OnClusterDelete(ctx, r.Client, instance); err != nil { - return result, err - } + err = finalize.OnClusterDelete(ctx, r.Client, &instance) return } if instance.Spec.ParsingError != "" { - return result, &parsingError{instance.Spec.ParsingError, "vtcluster"} + err = &parsingError{instance.Spec.ParsingError, "vtcluster"} + return } - if err := finalize.AddFinalizer(ctx, r.Client, instance); err != nil { - return result, err + if err = finalize.AddFinalizer(ctx, r.Client, &instance); err != nil { + return } - r.Client.Scheme().Default(instance) + r.Client.Scheme().Default(&instance) result, err = reconcileAndTrackStatus(ctx, r.Client, instance.DeepCopy(), func() (ctrl.Result, error) { - if err := vtcluster.CreateOrUpdate(ctx, r, instance); err != nil { + if err := vtcluster.CreateOrUpdate(ctx, r, &instance); err != nil { return result, fmt.Errorf("failed create or update vtcluster: %w", err) } return result, nil diff --git a/internal/controller/operator/vtsingle_controller.go b/internal/controller/operator/vtsingle_controller.go index 5eecbcbc6..0f328e65f 100644 --- a/internal/controller/operator/vtsingle_controller.go +++ b/internal/controller/operator/vtsingle_controller.go @@ -59,33 +59,33 @@ func (r *VTSingleReconciler) Init(rclient client.Client, l logr.Logger, sc *runt func (r *VTSingleReconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ctrl.Result, err error) { l := r.Log.WithValues("vtsingle", req.Name, "namespace", req.Namespace) ctx = logger.AddToContext(ctx, l) - instance := &vmv1.VTSingle{} + var instance vmv1.VTSingle defer func() { - result, err = handleReconcileErr(ctx, r.Client, instance, result, err) + result, err = handleReconcileErr(ctx, r.Client, &instance, result, err) }() - if err := r.Get(ctx, req.NamespacedName, instance); err != nil { - return result, &getError{err, "vtsingle", req} + if err = r.Get(ctx, req.NamespacedName, &instance); err != nil { + err = &getError{err, "vtsingle", req} + return } - RegisterObjectStat(instance, "vtsingle") + RegisterObjectStat(&instance, "vtsingle") if !instance.DeletionTimestamp.IsZero() { - if err := finalize.OnVTSingleDelete(ctx, r.Client, instance); err != nil { - return result, err - } + err = finalize.OnVTSingleDelete(ctx, r.Client, &instance) return } if instance.Spec.ParsingError != "" { - return result, &parsingError{instance.Spec.ParsingError, "vtsingle"} + err = &parsingError{instance.Spec.ParsingError, "vtsingle"} + return } - if err := finalize.AddFinalizer(ctx, r.Client, instance); err != nil { - return result, err + if err = finalize.AddFinalizer(ctx, r.Client, &instance); err != nil { + return } - r.Client.Scheme().Default(instance) + r.Client.Scheme().Default(&instance) result, err = reconcileAndTrackStatus(ctx, r.Client, instance.DeepCopy(), func() (ctrl.Result, error) { - if err := vtsingle.CreateOrUpdate(ctx, r, instance); err != nil { + if err := vtsingle.CreateOrUpdate(ctx, r, &instance); err != nil { return result, fmt.Errorf("failed create or update vtsingle: %w", err) } return result, nil From 947ac9331341557e86208f5663c7be183e187056 Mon Sep 17 00:00:00 2001 From: Andrii Chubatiuk Date: Mon, 30 Mar 2026 12:49:49 +0300 Subject: [PATCH 10/22] defaults: do not use clusterversion for vl/vt requests load balancer tag --- internal/controller/operator/factory/build/defaults.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/controller/operator/factory/build/defaults.go b/internal/controller/operator/factory/build/defaults.go index 834f46bee..1e126d7ab 100644 --- a/internal/controller/operator/factory/build/defaults.go +++ b/internal/controller/operator/factory/build/defaults.go @@ -569,6 +569,7 @@ func addVTClusterDefaults(objI any) { } if cr.Spec.RequestsLoadBalancer.Enabled { + cp.tag = "" addRequestsLoadBalancerDefaults(&cr.Spec.RequestsLoadBalancer, &cp) } } @@ -601,6 +602,7 @@ func addVLClusterDefaults(objI any) { addDefaultsToCommonParams(&cr.Spec.VLSelect.CommonAppsParams, &cp, &cv) } if cr.Spec.RequestsLoadBalancer.Enabled { + cp.tag = "" addRequestsLoadBalancerDefaults(&cr.Spec.RequestsLoadBalancer, &cp) } } From f786b8e80d24353ebd9a6b6a86ee46ef2df260f4 Mon Sep 17 00:00:00 2001 From: Andrii Chubatiuk Date: Mon, 30 Mar 2026 14:34:51 +0300 Subject: [PATCH 11/22] do not use cluster tag for vm lb --- internal/controller/operator/factory/build/defaults.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/controller/operator/factory/build/defaults.go b/internal/controller/operator/factory/build/defaults.go index 1e126d7ab..8b21e2a97 100644 --- a/internal/controller/operator/factory/build/defaults.go +++ b/internal/controller/operator/factory/build/defaults.go @@ -428,6 +428,7 @@ func addVMClusterDefaults(objI any) { addDefaultsToCommonParams(&cr.Spec.VMSelect.CommonAppsParams, &cp, &cv) } if cr.Spec.RequestsLoadBalancer.Enabled { + cp.tag = "" addRequestsLoadBalancerDefaults(&cr.Spec.RequestsLoadBalancer, &cp) } } From 69e3657676784b81263b6e6408d0d6d2e123223b Mon Sep 17 00:00:00 2001 From: Vadim Rutkovsky Date: Mon, 30 Mar 2026 16:44:30 +0200 Subject: [PATCH 12/22] fix: sort any unhealthy zone first in VMDistributed (#2009) Any zone which is not Operating - that is Failed on Extending - should be picked up first. The reason for this is that timed out zone update won't change the zone status to Failed and it would be considered functioning. This commit also adds unit tests for zone sorting --- .../operator/factory/vmdistributed/zone.go | 6 +- .../factory/vmdistributed/zone_test.go | 186 ++++++++++++++++++ 2 files changed, 190 insertions(+), 2 deletions(-) diff --git a/internal/controller/operator/factory/vmdistributed/zone.go b/internal/controller/operator/factory/vmdistributed/zone.go index bdb695752..12d9f19ad 100644 --- a/internal/controller/operator/factory/vmdistributed/zone.go +++ b/internal/controller/operator/factory/vmdistributed/zone.go @@ -45,8 +45,10 @@ func (zs *zones) Len() int { func (zs *zones) Less(i, j int) bool { statusI := zs.vmclusters[i].Status statusJ := zs.vmclusters[j].Status - if statusI.UpdateStatus != statusJ.UpdateStatus { - return statusI.UpdateStatus == vmv1beta1.UpdateStatusFailed + isNonOperationalI := statusI.UpdateStatus != vmv1beta1.UpdateStatusOperational + isNonOperationalJ := statusJ.UpdateStatus != vmv1beta1.UpdateStatusOperational + if isNonOperationalI != isNonOperationalJ { + return isNonOperationalI } isZeroI := zs.vmclusters[i].CreationTimestamp.IsZero() isZeroJ := zs.vmclusters[j].CreationTimestamp.IsZero() diff --git a/internal/controller/operator/factory/vmdistributed/zone_test.go b/internal/controller/operator/factory/vmdistributed/zone_test.go index 3cf3b95c1..22852d3bf 100644 --- a/internal/controller/operator/factory/vmdistributed/zone_test.go +++ b/internal/controller/operator/factory/vmdistributed/zone_test.go @@ -7,6 +7,7 @@ import ( "net/http" "net/http/httptest" "net/url" + "sort" "strconv" "sync" "testing" @@ -245,3 +246,188 @@ func TestWaitForEmptyPQ(t *testing.T) { errMsg: "context deadline exceeded", }) } + +func TestZonesSorting(t *testing.T) { + now := metav1.Now() + + type opts struct { + clusters []*vmv1beta1.VMCluster + hasChanges []bool + wantNames []string + validate func(*zones) + } + + f := func(o opts) { + t.Helper() + zs := &zones{ + vmclusters: o.clusters, + vmagents: make([]*vmv1beta1.VMAgent, len(o.clusters)), + hasChanges: make([]bool, len(o.clusters)), + } + for i := range o.clusters { + zs.vmagents[i] = &vmv1beta1.VMAgent{ + ObjectMeta: metav1.ObjectMeta{Name: o.clusters[i].Name}, + } + } + if o.hasChanges != nil { + zs.hasChanges = o.hasChanges + } + sort.Sort(zs) + names := make([]string, len(zs.vmclusters)) + for i, c := range zs.vmclusters { + names[i] = c.Name + } + assert.Equal(t, o.wantNames, names) + if o.validate != nil { + o.validate(zs) + } + } + + // failed zones sort before operational + f(opts{ + clusters: []*vmv1beta1.VMCluster{ + { + ObjectMeta: metav1.ObjectMeta{Name: "zone-a", CreationTimestamp: now}, + Status: vmv1beta1.VMClusterStatus{StatusMetadata: vmv1beta1.StatusMetadata{UpdateStatus: vmv1beta1.UpdateStatusOperational}}, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "zone-b", CreationTimestamp: now}, + Status: vmv1beta1.VMClusterStatus{StatusMetadata: vmv1beta1.StatusMetadata{UpdateStatus: vmv1beta1.UpdateStatusFailed}}, + }, + }, + wantNames: []string{"zone-b", "zone-a"}, + }) + + // failed and expanding zones are both non-operational: fall through to next criteria (name) + f(opts{ + clusters: []*vmv1beta1.VMCluster{ + { + ObjectMeta: metav1.ObjectMeta{Name: "zone-b", CreationTimestamp: now}, + Status: vmv1beta1.VMClusterStatus{StatusMetadata: vmv1beta1.StatusMetadata{UpdateStatus: vmv1beta1.UpdateStatusFailed}}, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "zone-a", CreationTimestamp: now}, + Status: vmv1beta1.VMClusterStatus{StatusMetadata: vmv1beta1.StatusMetadata{UpdateStatus: vmv1beta1.UpdateStatusExpanding}}, + }, + }, + wantNames: []string{"zone-a", "zone-b"}, + }) + + // expanding zones sort before operational + f(opts{ + clusters: []*vmv1beta1.VMCluster{ + { + ObjectMeta: metav1.ObjectMeta{Name: "zone-a", CreationTimestamp: now}, + Status: vmv1beta1.VMClusterStatus{StatusMetadata: vmv1beta1.StatusMetadata{UpdateStatus: vmv1beta1.UpdateStatusOperational}}, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "zone-b", CreationTimestamp: now}, + Status: vmv1beta1.VMClusterStatus{StatusMetadata: vmv1beta1.StatusMetadata{UpdateStatus: vmv1beta1.UpdateStatusExpanding}}, + }, + }, + wantNames: []string{"zone-b", "zone-a"}, + }) + + // new zones (zero CreationTimestamp) sort before existing + f(opts{ + clusters: []*vmv1beta1.VMCluster{ + { + ObjectMeta: metav1.ObjectMeta{Name: "zone-existing", CreationTimestamp: now}, + Status: vmv1beta1.VMClusterStatus{StatusMetadata: vmv1beta1.StatusMetadata{UpdateStatus: vmv1beta1.UpdateStatusOperational}}, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "zone-new"}, + Status: vmv1beta1.VMClusterStatus{StatusMetadata: vmv1beta1.StatusMetadata{UpdateStatus: vmv1beta1.UpdateStatusOperational}}, + }, + }, + wantNames: []string{"zone-new", "zone-existing"}, + }) + + // higher observedGeneration sorts first + f(opts{ + clusters: []*vmv1beta1.VMCluster{ + { + ObjectMeta: metav1.ObjectMeta{Name: "zone-low-gen", CreationTimestamp: now}, + Status: vmv1beta1.VMClusterStatus{ + StatusMetadata: vmv1beta1.StatusMetadata{UpdateStatus: vmv1beta1.UpdateStatusOperational, ObservedGeneration: 1}, + }, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "zone-high-gen", CreationTimestamp: now}, + Status: vmv1beta1.VMClusterStatus{ + StatusMetadata: vmv1beta1.StatusMetadata{UpdateStatus: vmv1beta1.UpdateStatusOperational, ObservedGeneration: 5}, + }, + }, + }, + wantNames: []string{"zone-high-gen", "zone-low-gen"}, + }) + + // equal status and generation sorts by name ascending + f(opts{ + clusters: []*vmv1beta1.VMCluster{ + { + ObjectMeta: metav1.ObjectMeta{Name: "zone-b", CreationTimestamp: now}, + Status: vmv1beta1.VMClusterStatus{ + StatusMetadata: vmv1beta1.StatusMetadata{UpdateStatus: vmv1beta1.UpdateStatusOperational, ObservedGeneration: 3}, + }, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "zone-a", CreationTimestamp: now}, + Status: vmv1beta1.VMClusterStatus{ + StatusMetadata: vmv1beta1.StatusMetadata{UpdateStatus: vmv1beta1.UpdateStatusOperational, ObservedGeneration: 3}, + }, + }, + }, + wantNames: []string{"zone-a", "zone-b"}, + }) + + // priority order: status > zero-timestamp > generation > name + f(opts{ + clusters: []*vmv1beta1.VMCluster{ + { + ObjectMeta: metav1.ObjectMeta{Name: "zone-operational-gen1", CreationTimestamp: now}, + Status: vmv1beta1.VMClusterStatus{ + StatusMetadata: vmv1beta1.StatusMetadata{UpdateStatus: vmv1beta1.UpdateStatusOperational, ObservedGeneration: 1}, + }, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "zone-operational-gen5", CreationTimestamp: now}, + Status: vmv1beta1.VMClusterStatus{ + StatusMetadata: vmv1beta1.StatusMetadata{UpdateStatus: vmv1beta1.UpdateStatusOperational, ObservedGeneration: 5}, + }, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "zone-new"}, + Status: vmv1beta1.VMClusterStatus{StatusMetadata: vmv1beta1.StatusMetadata{UpdateStatus: vmv1beta1.UpdateStatusOperational}}, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "zone-failed", CreationTimestamp: now}, + Status: vmv1beta1.VMClusterStatus{StatusMetadata: vmv1beta1.StatusMetadata{UpdateStatus: vmv1beta1.UpdateStatusFailed}}, + }, + }, + wantNames: []string{"zone-failed", "zone-new", "zone-operational-gen5", "zone-operational-gen1"}, + }) + + // swap keeps vmagents and hasChanges in sync + f(opts{ + clusters: []*vmv1beta1.VMCluster{ + { + ObjectMeta: metav1.ObjectMeta{Name: "zone-b", CreationTimestamp: now}, + Status: vmv1beta1.VMClusterStatus{StatusMetadata: vmv1beta1.StatusMetadata{UpdateStatus: vmv1beta1.UpdateStatusOperational}}, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "zone-a", CreationTimestamp: now}, + Status: vmv1beta1.VMClusterStatus{StatusMetadata: vmv1beta1.StatusMetadata{UpdateStatus: vmv1beta1.UpdateStatusOperational}}, + }, + }, + hasChanges: []bool{true, false}, + wantNames: []string{"zone-a", "zone-b"}, + validate: func(zs *zones) { + assert.Equal(t, zs.vmclusters[0].Name, zs.vmagents[0].Name) + assert.Equal(t, zs.vmclusters[1].Name, zs.vmagents[1].Name) + assert.False(t, zs.hasChanges[0]) + assert.True(t, zs.hasChanges[1]) + }, + }) + +} From 22f7612ec770bce5730ad83392b67f4eddaeaa1e Mon Sep 17 00:00:00 2001 From: Andrii Chubatiuk Date: Mon, 30 Mar 2026 19:45:51 +0300 Subject: [PATCH 13/22] vmagent,vmanomaly: render SHARD_NUM placeholder when shards count is positive number (#2002) Co-authored-by: Vadim Rutkovsky --- api/operator/v1/vmanomaly_types.go | 2 +- api/operator/v1beta1/vmagent_types.go | 2 +- docs/CHANGELOG.md | 1 + .../operator/factory/vmagent/vmagent_test.go | 119 +++++++++++------- .../factory/vmanomaly/statefulset_test.go | 94 ++++++++++++-- 5 files changed, 163 insertions(+), 55 deletions(-) diff --git a/api/operator/v1/vmanomaly_types.go b/api/operator/v1/vmanomaly_types.go index 0a94104b1..913ff0405 100644 --- a/api/operator/v1/vmanomaly_types.go +++ b/api/operator/v1/vmanomaly_types.go @@ -437,7 +437,7 @@ func (cr *VMAnomaly) Validate() error { // IsSharded returns true if sharding is enabled func (cr *VMAnomaly) IsSharded() bool { - return cr != nil && cr.Spec.ShardCount != nil && *cr.Spec.ShardCount > 1 + return cr != nil && cr.Spec.ShardCount != nil && *cr.Spec.ShardCount > 0 } // GetShardCount returns shard count for vmanomaly diff --git a/api/operator/v1beta1/vmagent_types.go b/api/operator/v1beta1/vmagent_types.go index 3bbf9888b..519aec153 100644 --- a/api/operator/v1beta1/vmagent_types.go +++ b/api/operator/v1beta1/vmagent_types.go @@ -209,7 +209,7 @@ func (cr *VMAgent) Validate() error { // IsSharded returns true if sharding is enabled func (cr *VMAgent) IsSharded() bool { - return cr != nil && cr.Spec.ShardCount != nil && *cr.Spec.ShardCount > 1 && !cr.Spec.DaemonSetMode + return cr != nil && cr.Spec.ShardCount != nil && *cr.Spec.ShardCount > 0 && !cr.Spec.DaemonSetMode } // GetShardCount returns shard count for vmagent diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index dab965407..6f2a2f8a7 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -18,6 +18,7 @@ aliases: * BUGFIX: [vmoperator](https://docs.victoriametrics.com/operator/): wait till PVC resize finished. See [#1970](https://github.com/VictoriaMetrics/operator/issues/1970). * BUGFIX: [vmalertmanager](https://docs.victoriametrics.com/operator/resources/vmalertmanager/): fixed ignored tracing config, when no alertmanagerconfig CRs collected. See [#1983](https://github.com/VictoriaMetrics/operator/issues/1983). * BUGFIX: [vmagent](https://docs.victoriametrics.com/operator/resources/vmagent/): apply scrape class relabellings before job ones. See [#1997](https://github.com/VictoriaMetrics/operator/issues/1997). +* BUGFIX: [vmanomaly](https://docs.victoriametrics.com/operator/resources/vmanomaly/) and [vmagent](https://docs.victoriametrics.com/operator/resources/vmagent/): render %SHARD_NUM% placeholder when shard count is greater than 0. See [#2001](https://github.com/VictoriaMetrics/operator/issues/2001). ## [v0.68.3](https://github.com/VictoriaMetrics/operator/releases/tag/v0.68.3) **Release date:** 16 March 2026 diff --git a/internal/controller/operator/factory/vmagent/vmagent_test.go b/internal/controller/operator/factory/vmagent/vmagent_test.go index 6a3ed6a09..66f4aa534 100644 --- a/internal/controller/operator/factory/vmagent/vmagent_test.go +++ b/internal/controller/operator/factory/vmagent/vmagent_test.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "sort" + "strconv" "strings" "testing" @@ -17,6 +18,7 @@ import ( "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/utils/ptr" + "sigs.k8s.io/controller-runtime/pkg/client" vmv1beta1 "github.com/VictoriaMetrics/operator/api/operator/v1beta1" "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/build" @@ -26,8 +28,7 @@ import ( func TestCreateOrUpdate(t *testing.T) { type opts struct { cr *vmv1beta1.VMAgent - validate func(set *appsv1.StatefulSet) - statefulsetMode bool + validate func(ctx context.Context, client client.Client, cr *vmv1beta1.VMAgent) wantErr bool predefinedObjects []runtime.Object } @@ -44,10 +45,8 @@ func TestCreateOrUpdate(t *testing.T) { } else { assert.NoError(t, err) } - if o.statefulsetMode && o.cr.Spec.ShardCount == nil { - var got appsv1.StatefulSet - assert.NoError(t, fclient.Get(context.Background(), types.NamespacedName{Namespace: o.cr.Namespace, Name: o.cr.PrefixedName()}, &got)) - o.validate(&got) + if o.validate != nil { + o.validate(ctx, fclient, o.cr) } } @@ -98,23 +97,24 @@ func TestCreateOrUpdate(t *testing.T) { }, }, }, - validate: func(got *appsv1.StatefulSet) { - assert.Equal(t, 1, len(got.Spec.Template.Spec.Containers)) - assert.Equal(t, 2, len(got.Spec.VolumeClaimTemplates)) - assert.Equal(t, "embed-sc", *got.Spec.VolumeClaimTemplates[0].Spec.StorageClassName) - assert.Equal(t, got.Spec.VolumeClaimTemplates[0].Spec.Resources, corev1.VolumeResourceRequirements{ + validate: func(ctx context.Context, fclient client.Client, cr *vmv1beta1.VMAgent) { + var sts appsv1.StatefulSet + assert.NoError(t, fclient.Get(ctx, types.NamespacedName{Namespace: cr.Namespace, Name: cr.PrefixedName()}, &sts)) + assert.Equal(t, 1, len(sts.Spec.Template.Spec.Containers)) + assert.Equal(t, 2, len(sts.Spec.VolumeClaimTemplates)) + assert.Equal(t, "embed-sc", *sts.Spec.VolumeClaimTemplates[0].Spec.StorageClassName) + assert.Equal(t, sts.Spec.VolumeClaimTemplates[0].Spec.Resources, corev1.VolumeResourceRequirements{ Requests: map[corev1.ResourceName]resource.Quantity{ corev1.ResourceStorage: resource.MustParse("10Gi"), }, }) - assert.Equal(t, "default", *got.Spec.VolumeClaimTemplates[1].Spec.StorageClassName) - assert.Equal(t, got.Spec.VolumeClaimTemplates[1].Spec.Resources, corev1.VolumeResourceRequirements{ + assert.Equal(t, "default", *sts.Spec.VolumeClaimTemplates[1].Spec.StorageClassName) + assert.Equal(t, sts.Spec.VolumeClaimTemplates[1].Spec.Resources, corev1.VolumeResourceRequirements{ Requests: map[corev1.ResourceName]resource.Quantity{ corev1.ResourceStorage: resource.MustParse("2Gi"), }, }) }, - statefulsetMode: true, predefinedObjects: []runtime.Object{ k8stools.NewReadyDeployment("vmagent-example-agent", "default"), }, @@ -130,16 +130,36 @@ func TestCreateOrUpdate(t *testing.T) { Spec: vmv1beta1.VMAgentSpec{ CommonAppsParams: vmv1beta1.CommonAppsParams{ ReplicaCount: ptr.To(int32(1)), + Affinity: &corev1.Affinity{ + PodAntiAffinity: &corev1.PodAntiAffinity{ + RequiredDuringSchedulingIgnoredDuringExecution: []corev1.PodAffinityTerm{{ + LabelSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "shard-num": "%SHARD_NUM%", + }, + }, + TopologyKey: "kubernetes.io/hostname", + }}, + }, + }, }, RemoteWrite: []vmv1beta1.VMAgentRemoteWriteSpec{ {URL: "http://remote-write"}, }, - ShardCount: func() *int32 { i := int32(2); return &i }(), + ShardCount: ptr.To[int32](1), }, }, + validate: func(ctx context.Context, fclient client.Client, cr *vmv1beta1.VMAgent) { + for i := range *cr.Spec.ShardCount { + var dep appsv1.Deployment + name := fmt.Sprintf("%s-%d", cr.PrefixedName(), i) + assert.NoError(t, fclient.Get(ctx, types.NamespacedName{Namespace: cr.Namespace, Name: name}, &dep)) + assert.Len(t, dep.Spec.Template.Spec.Containers, 2) + assert.Equal(t, dep.Spec.Template.Spec.Affinity.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution[0].LabelSelector.MatchLabels["shard-num"], strconv.Itoa(int(i))) + } + }, predefinedObjects: []runtime.Object{ k8stools.NewReadyDeployment("vmagent-example-agent-0", "default"), - k8stools.NewReadyDeployment("vmagent-example-agent-1", "default"), }, }) @@ -427,10 +447,11 @@ func TestCreateOrUpdate(t *testing.T) { }, }, }, - validate: func(got *appsv1.StatefulSet) { - assert.Equal(t, got.Spec.ServiceName, "my-headless-additional-service") + validate: func(ctx context.Context, fclient client.Client, cr *vmv1beta1.VMAgent) { + var sts appsv1.StatefulSet + assert.NoError(t, fclient.Get(ctx, types.NamespacedName{Namespace: cr.Namespace, Name: cr.PrefixedName()}, &sts)) + assert.Equal(t, sts.Spec.ServiceName, "my-headless-additional-service") }, - statefulsetMode: true, predefinedObjects: []runtime.Object{ k8stools.NewReadyDeployment("vmagent-example-agent", "default"), }, @@ -488,23 +509,27 @@ func TestCreateOrUpdate(t *testing.T) { }, }, }, - validate: func(got *appsv1.StatefulSet) { - assert.Len(t, got.Spec.Template.Spec.Containers, 1) - assert.Len(t, got.Spec.VolumeClaimTemplates, 2) - assert.Equal(t, *got.Spec.VolumeClaimTemplates[0].Spec.StorageClassName, "embed-sc") - assert.Equal(t, got.Spec.VolumeClaimTemplates[0].Spec.Resources, corev1.VolumeResourceRequirements{ - Requests: map[corev1.ResourceName]resource.Quantity{ - corev1.ResourceStorage: resource.MustParse("10Gi"), - }, - }) - assert.Equal(t, *got.Spec.VolumeClaimTemplates[1].Spec.StorageClassName, "default") - assert.Equal(t, got.Spec.VolumeClaimTemplates[1].Spec.Resources, corev1.VolumeResourceRequirements{ - Requests: map[corev1.ResourceName]resource.Quantity{ - corev1.ResourceStorage: resource.MustParse("2Gi"), - }, - }) + validate: func(ctx context.Context, fclient client.Client, cr *vmv1beta1.VMAgent) { + for i := range *cr.Spec.ShardCount { + var sts appsv1.StatefulSet + name := fmt.Sprintf("%s-%d", cr.PrefixedName(), i) + assert.NoError(t, fclient.Get(ctx, types.NamespacedName{Namespace: cr.Namespace, Name: name}, &sts)) + assert.Len(t, sts.Spec.Template.Spec.Containers, 1) + assert.Len(t, sts.Spec.VolumeClaimTemplates, 2) + assert.Equal(t, *sts.Spec.VolumeClaimTemplates[0].Spec.StorageClassName, "embed-sc") + assert.Equal(t, sts.Spec.VolumeClaimTemplates[0].Spec.Resources, corev1.VolumeResourceRequirements{ + Requests: map[corev1.ResourceName]resource.Quantity{ + corev1.ResourceStorage: resource.MustParse("10Gi"), + }, + }) + assert.Equal(t, *sts.Spec.VolumeClaimTemplates[1].Spec.StorageClassName, "default") + assert.Equal(t, sts.Spec.VolumeClaimTemplates[1].Spec.Resources, corev1.VolumeResourceRequirements{ + Requests: map[corev1.ResourceName]resource.Quantity{ + corev1.ResourceStorage: resource.MustParse("2Gi"), + }, + }) + } }, - statefulsetMode: true, }) // generate vmagent statefulset with prevSpec @@ -554,23 +579,24 @@ func TestCreateOrUpdate(t *testing.T) { }, }, }, - validate: func(got *appsv1.StatefulSet) { - assert.Len(t, got.Spec.Template.Spec.Containers, 1) - assert.Len(t, got.Spec.VolumeClaimTemplates, 2) - assert.Equal(t, *got.Spec.VolumeClaimTemplates[0].Spec.StorageClassName, "embed-sc") - assert.Equal(t, got.Spec.VolumeClaimTemplates[0].Spec.Resources, corev1.VolumeResourceRequirements{ + validate: func(ctx context.Context, fclient client.Client, cr *vmv1beta1.VMAgent) { + var sts appsv1.StatefulSet + assert.NoError(t, fclient.Get(ctx, types.NamespacedName{Namespace: cr.Namespace, Name: cr.PrefixedName()}, &sts)) + assert.Len(t, sts.Spec.Template.Spec.Containers, 1) + assert.Len(t, sts.Spec.VolumeClaimTemplates, 2) + assert.Equal(t, *sts.Spec.VolumeClaimTemplates[0].Spec.StorageClassName, "embed-sc") + assert.Equal(t, sts.Spec.VolumeClaimTemplates[0].Spec.Resources, corev1.VolumeResourceRequirements{ Requests: map[corev1.ResourceName]resource.Quantity{ corev1.ResourceStorage: resource.MustParse("10Gi"), }, }) - assert.Equal(t, *got.Spec.VolumeClaimTemplates[1].Spec.StorageClassName, "default") - assert.Equal(t, got.Spec.VolumeClaimTemplates[1].Spec.Resources, corev1.VolumeResourceRequirements{ + assert.Equal(t, *sts.Spec.VolumeClaimTemplates[1].Spec.StorageClassName, "default") + assert.Equal(t, sts.Spec.VolumeClaimTemplates[1].Spec.Resources, corev1.VolumeResourceRequirements{ Requests: map[corev1.ResourceName]resource.Quantity{ corev1.ResourceStorage: resource.MustParse("2Gi"), }, }) }, - statefulsetMode: true, }) // with oauth2 rw @@ -622,9 +648,10 @@ func TestCreateOrUpdate(t *testing.T) { }, }, }, - statefulsetMode: true, - validate: func(set *appsv1.StatefulSet) { - cnt := set.Spec.Template.Spec.Containers[0] + validate: func(ctx context.Context, fclient client.Client, cr *vmv1beta1.VMAgent) { + var sts appsv1.StatefulSet + assert.NoError(t, fclient.Get(ctx, types.NamespacedName{Namespace: cr.Namespace, Name: cr.PrefixedName()}, &sts)) + cnt := sts.Spec.Template.Spec.Containers[0] assert.Equal(t, cnt.Name, "vmagent") hasClientSecretArg := false for _, arg := range cnt.Args { diff --git a/internal/controller/operator/factory/vmanomaly/statefulset_test.go b/internal/controller/operator/factory/vmanomaly/statefulset_test.go index 3c6a573d8..981b69623 100644 --- a/internal/controller/operator/factory/vmanomaly/statefulset_test.go +++ b/internal/controller/operator/factory/vmanomaly/statefulset_test.go @@ -2,6 +2,8 @@ package vmanomaly import ( "context" + "fmt" + "strconv" "testing" "github.com/stretchr/testify/assert" @@ -23,7 +25,7 @@ import ( func TestCreateOrUpdate(t *testing.T) { type opts struct { cr *vmv1.VMAnomaly - validate func(set *appsv1.StatefulSet) + validate func(sts *appsv1.StatefulSet, idx int) wantErr bool predefinedObjects []runtime.Object } @@ -40,9 +42,19 @@ func TestCreateOrUpdate(t *testing.T) { assert.NoError(t, err) } if o.validate != nil { - var got appsv1.StatefulSet - assert.NoError(t, fclient.Get(ctx, types.NamespacedName{Namespace: o.cr.Namespace, Name: o.cr.PrefixedName()}, &got)) - o.validate(&got) + shardCount := ptr.Deref(o.cr.Spec.ShardCount, 0) + if shardCount > 0 { + for i := range shardCount { + var got appsv1.StatefulSet + name := fmt.Sprintf("%s-%d", o.cr.PrefixedName(), i) + assert.NoError(t, fclient.Get(ctx, types.NamespacedName{Namespace: o.cr.Namespace, Name: name}, &got)) + o.validate(&got, int(i)) + } + } else { + var got appsv1.StatefulSet + assert.NoError(t, fclient.Get(ctx, types.NamespacedName{Namespace: o.cr.Namespace, Name: o.cr.PrefixedName()}, &got)) + o.validate(&got, 0) + } } } @@ -90,7 +102,7 @@ schedulers: }, }, }, - validate: func(set *appsv1.StatefulSet) { + validate: func(set *appsv1.StatefulSet, _ int) { assert.Equal(t, set.Name, "vmanomaly-test-anomaly") assert.Equal(t, set.Spec.Template.Spec.Containers[0].Resources, corev1.ResourceRequirements{ Limits: corev1.ResourceList{ @@ -156,7 +168,7 @@ schedulers: }, }, }, - validate: func(set *appsv1.StatefulSet) { + validate: func(set *appsv1.StatefulSet, _ int) { assert.Len(t, set.Spec.Template.Spec.Containers, 1) container := set.Spec.Template.Spec.Containers[0] assert.Equal(t, container.Name, "vmanomaly") @@ -211,12 +223,80 @@ schedulers: }, }, }, - validate: func(set *appsv1.StatefulSet) { + validate: func(set *appsv1.StatefulSet, _ int) { + assert.Len(t, set.Spec.Template.Spec.Containers, 1) + container := set.Spec.Template.Spec.Containers[0] + expectedPath := "/custom-prefix/health" + assert.Equal(t, container.LivenessProbe.HTTPGet.Path, expectedPath) + assert.Equal(t, container.ReadinessProbe.HTTPGet.Path, expectedPath) + }, + }) + + // vmanomaly with shards enabled + f(opts{ + cr: &vmv1.VMAnomaly{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-anomaly", + Namespace: "monitoring", + Annotations: map[string]string{"not": "touch"}, + Labels: map[string]string{"main": "system"}, + }, + Spec: vmv1.VMAnomalySpec{ + CommonAppsParams: vmv1beta1.CommonAppsParams{ + ReplicaCount: ptr.To(int32(1)), + Affinity: &corev1.Affinity{ + PodAntiAffinity: &corev1.PodAntiAffinity{ + RequiredDuringSchedulingIgnoredDuringExecution: []corev1.PodAffinityTerm{{ + LabelSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "shard-num": "%SHARD_NUM%", + }, + }, + TopologyKey: "kubernetes.io/hostname", + }}, + }, + }, + }, + ShardCount: ptr.To[int32](1), + License: &vmv1beta1.License{ + Key: ptr.To("test"), + }, + ConfigRawYaml: ` +reader: + queries: + query_alias2: + expr: vm_metric +models: + model_univariate_1: + class: 'zscore' + z_threshold: 2.5 + queries: ['query_alias2'] +schedulers: + scheduler_periodic_1m: + class: "scheduler.periodic.PeriodicScheduler" + infer_every: 1m + fit_every: 2m + fit_window: 3h +`, + Reader: &vmv1.VMAnomalyReadersSpec{ + DatasourceURL: "http://test.com", + SamplingPeriod: "1m", + }, + Writer: &vmv1.VMAnomalyWritersSpec{ + DatasourceURL: "http://write.endpoint", + }, + Server: &vmv1.VMAnomalyServerSpec{ + PathPrefix: "custom-prefix", + }, + }, + }, + validate: func(set *appsv1.StatefulSet, idx int) { assert.Len(t, set.Spec.Template.Spec.Containers, 1) container := set.Spec.Template.Spec.Containers[0] expectedPath := "/custom-prefix/health" assert.Equal(t, container.LivenessProbe.HTTPGet.Path, expectedPath) assert.Equal(t, container.ReadinessProbe.HTTPGet.Path, expectedPath) + assert.Equal(t, set.Spec.Template.Spec.Affinity.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution[0].LabelSelector.MatchLabels["shard-num"], strconv.Itoa(idx)) }, }) } From 3f2bdeaa19a5d080789a776e3646e3430d1d4918 Mon Sep 17 00:00:00 2001 From: Vadim Rutkovsky Date: Mon, 30 Mar 2026 18:59:07 +0200 Subject: [PATCH 14/22] Rules rebalance tests and configmap / secrets list changes (#2011) * test: add rule rebalance tests * fix: reconcile objects when configmap/secret gets removed or changes a list of keys --- .../operator/factory/reconcile/configmap.go | 4 +- .../factory/reconcile/configmap_test.go | 73 +++++++++++++++++++ .../operator/factory/reconcile/secret.go | 3 +- .../operator/factory/reconcile/secret_test.go | 30 ++++++++ .../operator/factory/reconcile/vmagent.go | 2 +- .../factory/reconcile/vmagent_test.go | 64 ++++++++++++++-- .../operator/factory/reconcile/vmauth.go | 2 +- .../operator/factory/reconcile/vmauth_test.go | 54 ++++++++++++++ .../operator/factory/reconcile/vmcluster.go | 2 +- .../factory/reconcile/vmcluster_test.go | 41 +++++++++++ .../operator/factory/vmalert/rules_test.go | 58 +++++++++++++++ 11 files changed, 321 insertions(+), 12 deletions(-) diff --git a/internal/controller/operator/factory/reconcile/configmap.go b/internal/controller/operator/factory/reconcile/configmap.go index 97b88d9b7..f413c8e75 100644 --- a/internal/controller/operator/factory/reconcile/configmap.go +++ b/internal/controller/operator/factory/reconcile/configmap.go @@ -37,10 +37,10 @@ func ConfigMap(ctx context.Context, rclient client.Client, newObj *corev1.Config } logMessageMetadata := []string{fmt.Sprintf("name=%s", nsn.String())} - dataDiff := diffDeepDerivative(newObj.Data, existingObj.Data, "data") + dataDiff := diffDeep(newObj.Data, existingObj.Data, "data") needsUpdate := metaChanged || len(dataDiff) > 0 - binDataDiff := diffDeepDerivative(newObj.BinaryData, existingObj.BinaryData, "binaryData") + binDataDiff := diffDeep(newObj.BinaryData, existingObj.BinaryData, "binaryData") needsUpdate = needsUpdate || len(binDataDiff) > 0 if !needsUpdate { diff --git a/internal/controller/operator/factory/reconcile/configmap_test.go b/internal/controller/operator/factory/reconcile/configmap_test.go index 38e19c8de..ea21d216c 100644 --- a/internal/controller/operator/factory/reconcile/configmap_test.go +++ b/internal/controller/operator/factory/reconcile/configmap_test.go @@ -207,6 +207,79 @@ func TestConfigMapReconcile(t *testing.T) { }, }) + // data key added + f(opts{ + new: &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: nn.Name, + Namespace: nn.Namespace, + }, + Data: map[string]string{ + "rule-a.yaml": "groups: []", + "rule-b.yaml": "groups: []", + }, + }, + prevMeta: &metav1.ObjectMeta{ + Name: nn.Name, + Namespace: nn.Namespace, + }, + predefinedObjects: []runtime.Object{ + &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: nn.Name, + Namespace: nn.Namespace, + }, + Data: map[string]string{ + "rule-a.yaml": "groups: []", + }, + }, + }, + actions: []k8stools.ClientAction{ + {Verb: "Get", Kind: "ConfigMap", Resource: nn}, + {Verb: "Update", Kind: "ConfigMap", Resource: nn}, + }, + validate: func(c *corev1.ConfigMap) { + assert.Contains(t, c.Data, "rule-b.yaml") + }, + }) + + // data key removed + f(opts{ + new: &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: nn.Name, + Namespace: nn.Namespace, + }, + Data: map[string]string{ + "rule-a.yaml": "groups: []", + }, + }, + prevMeta: &metav1.ObjectMeta{ + Name: nn.Name, + Namespace: nn.Namespace, + }, + predefinedObjects: []runtime.Object{ + &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: nn.Name, + Namespace: nn.Namespace, + }, + Data: map[string]string{ + "rule-a.yaml": "groups: []", + "rule-b.yaml": "groups: []", + }, + }, + }, + actions: []k8stools.ClientAction{ + {Verb: "Get", Kind: "ConfigMap", Resource: nn}, + {Verb: "Update", Kind: "ConfigMap", Resource: nn}, + }, + validate: func(c *corev1.ConfigMap) { + _, exists := c.Data["rule-b.yaml"] + assert.False(t, exists, "rule-b.yaml should have been removed from configmap") + }, + }) + // no update with 3-rd party annotations f(opts{ new: &corev1.ConfigMap{ diff --git a/internal/controller/operator/factory/reconcile/secret.go b/internal/controller/operator/factory/reconcile/secret.go index b9c6db49b..3dbbacf20 100644 --- a/internal/controller/operator/factory/reconcile/secret.go +++ b/internal/controller/operator/factory/reconcile/secret.go @@ -6,7 +6,6 @@ import ( "strings" corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/equality" k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" @@ -36,7 +35,7 @@ func Secret(ctx context.Context, rclient client.Client, newObj *corev1.Secret, p return err } logMessageMetadata := []string{fmt.Sprintf("name=%s", nsn.String())} - isDataEqual := equality.Semantic.DeepDerivative(newObj.Data, existingObj.Data) + isDataEqual := len(diffDeep(newObj.Data, existingObj.Data, "data")) == 0 needsUpdate := metaChanged || !isDataEqual logMessageMetadata = append(logMessageMetadata, fmt.Sprintf("data_changed=%t", !isDataEqual)) diff --git a/internal/controller/operator/factory/reconcile/secret_test.go b/internal/controller/operator/factory/reconcile/secret_test.go index bebfcda1d..c6bf6d9b6 100644 --- a/internal/controller/operator/factory/reconcile/secret_test.go +++ b/internal/controller/operator/factory/reconcile/secret_test.go @@ -83,4 +83,34 @@ func TestSecretReconcile(t *testing.T) { {Verb: "Update", Kind: "Secret", Resource: nn}, }, }) + + // data key added + f(opts{ + new: getSecret(func(s *corev1.Secret) { + s.Data["key2"] = []byte("value2") + }), + prevMeta: &getSecret().ObjectMeta, + predefinedObjects: []runtime.Object{ + getSecret(), + }, + actions: []k8stools.ClientAction{ + {Verb: "Get", Kind: "Secret", Resource: nn}, + {Verb: "Update", Kind: "Secret", Resource: nn}, + }, + }) + + // data key removed + f(opts{ + new: getSecret(func(s *corev1.Secret) { + delete(s.Data, "key") + }), + prevMeta: &getSecret().ObjectMeta, + predefinedObjects: []runtime.Object{ + getSecret(), + }, + actions: []k8stools.ClientAction{ + {Verb: "Get", Kind: "Secret", Resource: nn}, + {Verb: "Update", Kind: "Secret", Resource: nn}, + }, + }) } diff --git a/internal/controller/operator/factory/reconcile/vmagent.go b/internal/controller/operator/factory/reconcile/vmagent.go index a85734128..3acf1dd93 100644 --- a/internal/controller/operator/factory/reconcile/vmagent.go +++ b/internal/controller/operator/factory/reconcile/vmagent.go @@ -44,7 +44,7 @@ func VMAgent(ctx context.Context, rclient client.Client, newObj, prevObj *vmv1be return err } logMessageMetadata := []string{fmt.Sprintf("name=%s, is_prev_nil=%t", nsn.String(), prevObj == nil)} - specDiff := diffDeepDerivative(newObj.Spec, existingObj.Spec, "spec") + specDiff := diffDeep(newObj.Spec, existingObj.Spec, "spec") needsUpdate := metaChanged || len(specDiff) > 0 if !needsUpdate { return nil diff --git a/internal/controller/operator/factory/reconcile/vmagent_test.go b/internal/controller/operator/factory/reconcile/vmagent_test.go index f9d003867..95ff0a746 100644 --- a/internal/controller/operator/factory/reconcile/vmagent_test.go +++ b/internal/controller/operator/factory/reconcile/vmagent_test.go @@ -126,22 +126,76 @@ func TestVMAgentReconcile(t *testing.T) { }, }) - // attempt to recreate on deletion + // configmaps added f(opts{ - new: getVMAgent(), + new: getVMAgent(func(v *vmv1beta1.VMAgent) { + v.Spec.ConfigMaps = []string{"cm1"} + }), + prev: getVMAgent(), predefinedObjects: []runtime.Object{ getVMAgent(func(v *vmv1beta1.VMAgent) { - v.Finalizers = []string{vmv1beta1.FinalizerName} - v.DeletionTimestamp = ptr.To(metav1.Now()) + v.Status.UpdateStatus = vmv1beta1.UpdateStatusOperational + v.Generation = 1 + v.Status.ObservedGeneration = 1 }), }, actions: []k8stools.ClientAction{ {Verb: "Get", Kind: "VMAgent", Resource: nn}, + {Verb: "Update", Kind: "VMAgent", Resource: nn}, {Verb: "Get", Kind: "VMAgent", Resource: nn}, + }, + validate: func(v *vmv1beta1.VMAgent) { + assert.Equal(t, []string{"cm1"}, v.Spec.ConfigMaps) + }, + }) + + // configmaps changed + f(opts{ + new: getVMAgent(func(v *vmv1beta1.VMAgent) { + v.Spec.ConfigMaps = []string{"cm2"} + }), + prev: getVMAgent(func(v *vmv1beta1.VMAgent) { + v.Spec.ConfigMaps = []string{"cm1"} + }), + predefinedObjects: []runtime.Object{ + getVMAgent(func(v *vmv1beta1.VMAgent) { + v.Spec.ConfigMaps = []string{"cm1"} + v.Status.UpdateStatus = vmv1beta1.UpdateStatusOperational + v.Generation = 1 + v.Status.ObservedGeneration = 1 + }), + }, + actions: []k8stools.ClientAction{ {Verb: "Get", Kind: "VMAgent", Resource: nn}, + {Verb: "Update", Kind: "VMAgent", Resource: nn}, {Verb: "Get", Kind: "VMAgent", Resource: nn}, + }, + validate: func(v *vmv1beta1.VMAgent) { + assert.Equal(t, []string{"cm2"}, v.Spec.ConfigMaps) + }, + }) + + // configmaps removed + f(opts{ + new: getVMAgent(), + prev: getVMAgent(func(v *vmv1beta1.VMAgent) { + v.Spec.ConfigMaps = []string{"cm1"} + }), + predefinedObjects: []runtime.Object{ + getVMAgent(func(v *vmv1beta1.VMAgent) { + v.Spec.ConfigMaps = []string{"cm1"} + v.Status.UpdateStatus = vmv1beta1.UpdateStatusOperational + v.Generation = 1 + v.Status.ObservedGeneration = 1 + }), + }, + actions: []k8stools.ClientAction{ {Verb: "Get", Kind: "VMAgent", Resource: nn}, + {Verb: "Update", Kind: "VMAgent", Resource: nn}, + {Verb: "Get", Kind: "VMAgent", Resource: nn}, + }, + validate: func(v *vmv1beta1.VMAgent) { + assert.Empty(t, v.Spec.ConfigMaps) }, - wantErr: true, }) } diff --git a/internal/controller/operator/factory/reconcile/vmauth.go b/internal/controller/operator/factory/reconcile/vmauth.go index 97c09caea..5afd2a877 100644 --- a/internal/controller/operator/factory/reconcile/vmauth.go +++ b/internal/controller/operator/factory/reconcile/vmauth.go @@ -43,7 +43,7 @@ func VMAuth(ctx context.Context, rclient client.Client, newObj, prevObj *vmv1bet return err } logMessageMetadata := []string{fmt.Sprintf("name=%s, is_prev_nil=%t", nsn.String(), prevObj == nil)} - specDiff := diffDeepDerivative(newObj.Spec, existingObj.Spec, "spec") + specDiff := diffDeep(newObj.Spec, existingObj.Spec, "spec") needsUpdate := metaChanged || len(specDiff) > 0 if !needsUpdate { return nil diff --git a/internal/controller/operator/factory/reconcile/vmauth_test.go b/internal/controller/operator/factory/reconcile/vmauth_test.go index 17358abb6..23c4fd366 100644 --- a/internal/controller/operator/factory/reconcile/vmauth_test.go +++ b/internal/controller/operator/factory/reconcile/vmauth_test.go @@ -95,4 +95,58 @@ func TestVMAuthReconcile(t *testing.T) { {Verb: "Get", Kind: "VMAuth", Resource: nn}, }, }) + + // no update on status change + f(opts{ + new: getVMAuth(), + prev: getVMAuth(), + predefinedObjects: []runtime.Object{ + getVMAuth(func(v *vmv1beta1.VMAuth) { + v.Status.UpdateStatus = vmv1beta1.UpdateStatusOperational + }), + }, + actions: []k8stools.ClientAction{ + {Verb: "Get", Kind: "VMAuth", Resource: nn}, + {Verb: "Get", Kind: "VMAuth", Resource: nn}, + }, + }) + + // configmaps added + f(opts{ + new: getVMAuth(func(v *vmv1beta1.VMAuth) { + v.Spec.ConfigMaps = []string{"cm1"} + }), + prev: getVMAuth(), + predefinedObjects: []runtime.Object{ + getVMAuth(func(v *vmv1beta1.VMAuth) { + v.Status.UpdateStatus = vmv1beta1.UpdateStatusOperational + v.Status.ObservedGeneration = v.Generation + }), + }, + actions: []k8stools.ClientAction{ + {Verb: "Get", Kind: "VMAuth", Resource: nn}, + {Verb: "Update", Kind: "VMAuth", Resource: nn}, + {Verb: "Get", Kind: "VMAuth", Resource: nn}, + }, + }) + + // configmaps removed + f(opts{ + new: getVMAuth(), + prev: getVMAuth(func(v *vmv1beta1.VMAuth) { + v.Spec.ConfigMaps = []string{"cm1"} + }), + predefinedObjects: []runtime.Object{ + getVMAuth(func(v *vmv1beta1.VMAuth) { + v.Spec.ConfigMaps = []string{"cm1"} + v.Status.UpdateStatus = vmv1beta1.UpdateStatusOperational + v.Status.ObservedGeneration = v.Generation + }), + }, + actions: []k8stools.ClientAction{ + {Verb: "Get", Kind: "VMAuth", Resource: nn}, + {Verb: "Update", Kind: "VMAuth", Resource: nn}, + {Verb: "Get", Kind: "VMAuth", Resource: nn}, + }, + }) } diff --git a/internal/controller/operator/factory/reconcile/vmcluster.go b/internal/controller/operator/factory/reconcile/vmcluster.go index 0c2336779..b85aa7547 100644 --- a/internal/controller/operator/factory/reconcile/vmcluster.go +++ b/internal/controller/operator/factory/reconcile/vmcluster.go @@ -43,7 +43,7 @@ func VMCluster(ctx context.Context, rclient client.Client, newObj, prevObj *vmv1 return err } logMessageMetadata := []string{fmt.Sprintf("name=%s, is_prev_nil=%t", nsn.String(), prevObj == nil)} - specDiff := diffDeepDerivative(newObj.Spec, existingObj.Spec, "spec") + specDiff := diffDeep(newObj.Spec, existingObj.Spec, "spec") needsUpdate := metaChanged || len(specDiff) > 0 if !needsUpdate { return nil diff --git a/internal/controller/operator/factory/reconcile/vmcluster_test.go b/internal/controller/operator/factory/reconcile/vmcluster_test.go index 8ca2f2488..f63dd354c 100644 --- a/internal/controller/operator/factory/reconcile/vmcluster_test.go +++ b/internal/controller/operator/factory/reconcile/vmcluster_test.go @@ -108,4 +108,45 @@ func TestVMClusterReconcile(t *testing.T) { {Verb: "Get", Kind: "VMCluster", Resource: nn}, }, }) + + // vmselect configmaps added + f(opts{ + new: getVMCluster(func(v *vmv1beta1.VMCluster) { + v.Spec.VMSelect = &vmv1beta1.VMSelect{CommonAppsParams: vmv1beta1.CommonAppsParams{ConfigMaps: []string{"cm1"}}} + }), + prev: getVMCluster(), + predefinedObjects: []runtime.Object{ + getVMCluster(func(v *vmv1beta1.VMCluster) { + v.Status.UpdateStatus = vmv1beta1.UpdateStatusOperational + v.Status.ObservedGeneration = v.Generation + }), + }, + actions: []k8stools.ClientAction{ + {Verb: "Get", Kind: "VMCluster", Resource: nn}, + {Verb: "Update", Kind: "VMCluster", Resource: nn}, + {Verb: "Get", Kind: "VMCluster", Resource: nn}, + }, + }) + + // vmselect configmaps removed + f(opts{ + new: getVMCluster(func(v *vmv1beta1.VMCluster) { + v.Spec.VMSelect = &vmv1beta1.VMSelect{} + }), + prev: getVMCluster(func(v *vmv1beta1.VMCluster) { + v.Spec.VMSelect = &vmv1beta1.VMSelect{CommonAppsParams: vmv1beta1.CommonAppsParams{ConfigMaps: []string{"cm1"}}} + }), + predefinedObjects: []runtime.Object{ + getVMCluster(func(v *vmv1beta1.VMCluster) { + v.Spec.VMSelect = &vmv1beta1.VMSelect{CommonAppsParams: vmv1beta1.CommonAppsParams{ConfigMaps: []string{"cm1"}}} + v.Status.UpdateStatus = vmv1beta1.UpdateStatusOperational + v.Status.ObservedGeneration = v.Generation + }), + }, + actions: []k8stools.ClientAction{ + {Verb: "Get", Kind: "VMCluster", Resource: nn}, + {Verb: "Update", Kind: "VMCluster", Resource: nn}, + {Verb: "Get", Kind: "VMCluster", Resource: nn}, + }, + }) } diff --git a/internal/controller/operator/factory/vmalert/rules_test.go b/internal/controller/operator/factory/vmalert/rules_test.go index 2e2bb6b67..571a27c98 100644 --- a/internal/controller/operator/factory/vmalert/rules_test.go +++ b/internal/controller/operator/factory/vmalert/rules_test.go @@ -8,6 +8,7 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" "k8s.io/utils/ptr" vmv1beta1 "github.com/VictoriaMetrics/operator/api/operator/v1beta1" @@ -258,6 +259,63 @@ func TestCreateOrUpdateRuleConfigMaps(t *testing.T) { }) } +func TestRuleRebalance(t *testing.T) { + ctx := context.Background() + + // Each rule's serialized YAML is ~76 bytes. + origLimit := vmv1beta1.MaxConfigMapDataSize + vmv1beta1.MaxConfigMapDataSize = 80 + defer func() { vmv1beta1.MaxConfigMapDataSize = origLimit }() + + mkRule := func(ns, name, recordName string) *vmv1beta1.VMRule { + return &vmv1beta1.VMRule{ + ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: ns}, + Spec: vmv1beta1.VMRuleSpec{ + Groups: []vmv1beta1.RuleGroup{{ + Name: name, + Rules: []vmv1beta1.Rule{{Record: recordName, Expr: "vector(1)"}}, + }}, + }, + } + } + + ns := "default" + cr := &vmv1beta1.VMAlert{ + ObjectMeta: metav1.ObjectMeta{Name: "recording", Namespace: ns}, + Spec: vmv1beta1.VMAlertSpec{SelectAllByDefault: true}, + } + + ruleB := mkRule(ns, "rule-b", "job:b:total") + + firstRuleCM := "vm-recording-rulefiles-0" + secondRuleCM := "vm-recording-rulefiles-1" + + // One rule fits the configmap + fclient := k8stools.GetTestClientWithObjects([]runtime.Object{ruleB}) + names, err := CreateOrUpdateRuleConfigMaps(ctx, fclient, cr, nil) + assert.NoError(t, err) + assert.Equal(t, []string{firstRuleCM}, names) + + var cm0 corev1.ConfigMap + var cm1 corev1.ConfigMap + assert.NoError(t, fclient.Get(ctx, types.NamespacedName{Name: firstRuleCM, Namespace: ns}, &cm0)) + assert.Contains(t, cm0.Data, "default-rule-b.yaml", "rule-b should be in cm-0 initially") + + // Bin-packing puts rule-a in cm-0 and spills rule-b to cm-1. + ruleA := mkRule(ns, "rule-a", "job:a:total") + assert.NoError(t, fclient.Create(ctx, ruleA)) + + names, err = CreateOrUpdateRuleConfigMaps(ctx, fclient, cr, nil) + assert.NoError(t, err) + assert.Equal(t, []string{firstRuleCM, secondRuleCM}, names) + assert.NoError(t, fclient.Get(ctx, types.NamespacedName{Name: firstRuleCM, Namespace: ns}, &cm0)) + assert.NoError(t, fclient.Get(ctx, types.NamespacedName{Name: secondRuleCM, Namespace: ns}, &cm1)) + + assert.Contains(t, cm0.Data, "default-rule-a.yaml") + assert.NotContains(t, cm0.Data, "default-rule-b.yaml", "rule-b must be removed from cm-0 after moving to cm-1") + assert.Contains(t, cm1.Data, "default-rule-b.yaml") +} + func Test_deduplicateRules(t *testing.T) { f := func(origin, want []*vmv1beta1.VMRule) { t.Helper() From 9c2f004661dc94caf322c1d9ee1c426d4727f205 Mon Sep 17 00:00:00 2001 From: Vadim Rutkovsky Date: Tue, 31 Mar 2026 07:17:11 +0200 Subject: [PATCH 15/22] fix: watch daemonsets for vmagent / vlagent (#2012) VMAgent / VLAgents may run in daemonset mode, so the controller should be reconciling them too --- api/operator/v1/vlagent_types.go | 3 ++- internal/controller/operator/vlagent_controller.go | 4 ++-- internal/controller/operator/vmagent_controller.go | 1 + 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/api/operator/v1/vlagent_types.go b/api/operator/v1/vlagent_types.go index efede59c8..6f8a229e2 100644 --- a/api/operator/v1/vlagent_types.go +++ b/api/operator/v1/vlagent_types.go @@ -296,7 +296,8 @@ func (cr *VLAgent) GetStatusMetadata() *vmv1beta1.StatusMetadata { // VLAgent - is a tiny but brave agent, which helps you collect logs from various sources and stores them in VictoriaLogs. // +operator-sdk:gen-csv:customresourcedefinitions.displayName="VLAgent App" -// +operator-sdk:gen-csv:customresourcedefinitions.resources="Deployment,apps" +// +operator-sdk:gen-csv:customresourcedefinitions.resources="DaemonSet,apps" +// +operator-sdk:gen-csv:customresourcedefinitions.resources="StatefulSet,apps" // +operator-sdk:gen-csv:customresourcedefinitions.resources="Service,v1" // +operator-sdk:gen-csv:customresourcedefinitions.resources="Secret,v1" // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object diff --git a/internal/controller/operator/vlagent_controller.go b/internal/controller/operator/vlagent_controller.go index 43c92d87a..12ec3ab1b 100644 --- a/internal/controller/operator/vlagent_controller.go +++ b/internal/controller/operator/vlagent_controller.go @@ -58,7 +58,7 @@ func (r *VLAgentReconciler) Init(rclient client.Client, l logr.Logger, sc *runti // +kubebuilder:rbac:groups="",resources=services,verbs=* // +kubebuilder:rbac:groups="",resources=services/finalizers,verbs=* // +kubebuilder:rbac:groups="",resources=serviceaccounts,verbs=get;create,update;list -// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=* +// +kubebuilder:rbac:groups=apps,resources=daemonsets,verbs=* // +kubebuilder:rbac:groups=apps,resources=statefulsets,verbs=* func (r *VLAgentReconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ctrl.Result, err error) { l := r.Log.WithValues("vlagent", req.Name, "namespace", req.Namespace) @@ -113,7 +113,7 @@ func (r *VLAgentReconciler) Scheme() *runtime.Scheme { func (r *VLAgentReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&vmv1.VLAgent{}). - Owns(&appsv1.Deployment{}). + Owns(&appsv1.DaemonSet{}). Owns(&appsv1.StatefulSet{}). Owns(&corev1.ServiceAccount{}). WithOptions(getDefaultOptions()). diff --git a/internal/controller/operator/vmagent_controller.go b/internal/controller/operator/vmagent_controller.go index 702571588..cfd3f8689 100644 --- a/internal/controller/operator/vmagent_controller.go +++ b/internal/controller/operator/vmagent_controller.go @@ -138,6 +138,7 @@ func (r *VMAgentReconciler) Scheme() *runtime.Scheme { func (r *VMAgentReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&vmv1beta1.VMAgent{}). + Owns(&appsv1.DaemonSet{}). Owns(&appsv1.Deployment{}). Owns(&appsv1.StatefulSet{}). Owns(&corev1.ServiceAccount{}). From 176c1a4a3669970bf75dad39968418f1e841d6e1 Mon Sep 17 00:00:00 2001 From: James Luck Date: Tue, 31 Mar 2026 18:05:42 +1100 Subject: [PATCH 16/22] Improving logging telemetry in the VMDistributed path (#1996) * Improving logging telemetry in the VMDistributed path: - Common log entries are now marked as "debug" level - waitForStatus now reports any errors periodically rather than waiting until the timeout occurs * Making changes suggested * applied suggestions --------- Co-authored-by: Andrii Chubatiuk --- .../operator/factory/reconcile/reconcile.go | 7 +++++++ .../operator/factory/vmdistributed/zone.go | 15 +++++++++++---- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/internal/controller/operator/factory/reconcile/reconcile.go b/internal/controller/operator/factory/reconcile/reconcile.go index 9991bf314..c4deac688 100644 --- a/internal/controller/operator/factory/reconcile/reconcile.go +++ b/internal/controller/operator/factory/reconcile/reconcile.go @@ -32,6 +32,7 @@ var ( appWaitReadyTimeout = 5 * time.Second vmWaitReadyInterval = 5 * time.Second + vmWaitLogInterval = 60 * time.Second ) // Init sets package defaults @@ -158,6 +159,7 @@ func waitForStatus[T client.Object, ST StatusWithMetadata[STC], STC any]( ) error { lastStatus := obj.GetStatusMetadata() nsn := types.NamespacedName{Name: obj.GetName(), Namespace: obj.GetNamespace()} + lastLogged := time.Now() err := wait.PollUntilContextCancel(ctx, interval, false, func(ctx context.Context) (done bool, err error) { if err = rclient.Get(ctx, nsn, obj); err != nil { if k8serrors.IsNotFound(err) { @@ -167,6 +169,11 @@ func waitForStatus[T client.Object, ST StatusWithMetadata[STC], STC any]( return } lastStatus = obj.GetStatusMetadata() + + if lastStatus != nil && time.Now().After(lastLogged.Add(vmWaitLogInterval)) { + logger.WithContext(ctx).V(1).Info(fmt.Sprintf("waiting for %T=%s to be ready, current status: %s", obj, nsn.String(), string(lastStatus.UpdateStatus))) + lastLogged = time.Now() + } return lastStatus != nil && obj.GetGeneration() == lastStatus.ObservedGeneration && lastStatus.UpdateStatus == status, nil }) if err != nil { diff --git a/internal/controller/operator/factory/vmdistributed/zone.go b/internal/controller/operator/factory/vmdistributed/zone.go index 12d9f19ad..bd72e55c9 100644 --- a/internal/controller/operator/factory/vmdistributed/zone.go +++ b/internal/controller/operator/factory/vmdistributed/zone.go @@ -297,6 +297,9 @@ func (zs *zones) waitForEmptyPQ(ctx context.Context, rclient client.Client, inte vmCluster := zs.vmclusters[clusterIdx] clusterURLHash := fmt.Sprintf("%016X", xxhash.Sum64([]byte(vmCluster.GetRemoteWriteURL()))) + nsnCluster := types.NamespacedName{Name: vmCluster.Name, Namespace: vmCluster.Namespace} + logger.WithContext(ctx).Info("ensuring persistent queues are drained", "name", nsnCluster.String()) + pollMetrics := func(pctx context.Context, nsn types.NamespacedName, addr string) error { return wait.PollUntilContextCancel(pctx, interval, true, func(ctx context.Context) (done bool, err error) { // Query each discovered ip. If any returns non-zero metric, continue polling. @@ -317,11 +320,11 @@ func (zs *zones) waitForEmptyPQ(ctx context.Context, rclient client.Client, inte continue } if v > 0 { - logger.WithContext(ctx).Info("persistent queue on VMAgent instance is not ready", "url", addr, "name", nsn.String(), "size", v) + logger.WithContext(ctx).V(1).Info("persistent queue on VMAgent instance is not ready", "url", addr, "name", nsn.String(), "size", v) return false, nil } } - logger.WithContext(ctx).Info("all persistent queues on VMAgent for given cluster were drained", "url", addr, "name", nsn.String()) + logger.WithContext(ctx).V(1).Info("all persistent queues on VMAgent for given cluster were drained", "url", addr, "name", nsn.String()) return true, nil }) } @@ -344,7 +347,7 @@ func (zs *zones) waitForEmptyPQ(ctx context.Context, rclient client.Client, inte if m.has(addr) { continue } - logger.WithContext(ctx).Info("start polling metrics from VMAgent instance", "url", addr, "name", nsn.String()) + logger.WithContext(ctx).V(1).Info("start polling metrics from VMAgent instance", "url", addr, "name", nsn.String()) pctx := m.add(addr) wg.Go(func() { if err := pollMetrics(pctx, nsn, addr); err != nil { @@ -355,7 +358,7 @@ func (zs *zones) waitForEmptyPQ(ctx context.Context, rclient client.Client, inte } for _, addr := range m.ids() { if _, ok := addrs[addr]; !ok { - logger.WithContext(ctx).Info("stop polling metrics from VMAgent instance", "url", addr, "name", nsn.String()) + logger.WithContext(ctx).V(1).Info("stop polling metrics from VMAgent instance", "url", addr, "name", nsn.String()) m.delete(addr) } } @@ -363,6 +366,10 @@ func (zs *zones) waitForEmptyPQ(ctx context.Context, rclient client.Client, inte }) } wg.Wait() + + if ctx.Err() == nil { + logger.WithContext(ctx).Info("all persistent queues were drained", "name", nsnCluster.String()) + } } func newManager(ctx context.Context) *manager { From e154084f07d3cf64250dc36b804d5305bcbbfb1d Mon Sep 17 00:00:00 2001 From: Andrii Chubatiuk Date: Tue, 31 Mar 2026 11:21:29 +0300 Subject: [PATCH 17/22] logging: made frequent vmrule, vmscrape logs verbose (#2014) Co-authored-by: Vadim Rutkovsky --- .../operator/factory/limiter/limiter.go | 68 +++++++---- .../operator/factory/limiter/limiter_test.go | 107 ++++++++++++++++++ .../operator/factory/logger/logger.go | 4 +- .../operator/factory/reconcile/reconcile.go | 7 +- .../operator/factory/reconcile/statefulset.go | 2 +- .../controller/operator/vmagent_controller.go | 4 +- .../controller/operator/vmalert_controller.go | 2 +- .../operator/vmalertmanager_controller.go | 2 +- .../vmalertmanagerconfig_controller.go | 2 +- .../controller/operator/vmauth_controller.go | 2 +- .../controller/operator/vmrule_controller.go | 2 +- .../controller/operator/vmuser_controller.go | 2 +- 12 files changed, 167 insertions(+), 37 deletions(-) create mode 100644 internal/controller/operator/factory/limiter/limiter_test.go diff --git a/internal/controller/operator/factory/limiter/limiter.go b/internal/controller/operator/factory/limiter/limiter.go index 95035789e..2d17a9c3c 100644 --- a/internal/controller/operator/factory/limiter/limiter.go +++ b/internal/controller/operator/factory/limiter/limiter.go @@ -8,42 +8,66 @@ import ( "sigs.k8s.io/controller-runtime/pkg/metrics" ) -// RateLimiter limits reconcile callback calls +// NewRateLimiter returns limiter with limit per duration +func NewRateLimiter(limit int64, duration time.Duration) *RateLimiter { + return &RateLimiter{ + limit: limit, + duration: duration, + } +} + +// RateLimiter limits callback calls type RateLimiter struct { - mu sync.Mutex - budget int64 - limit int64 - deadline time.Time - throttled prometheus.Counter + budget int64 + limit int64 + duration time.Duration + deadline time.Time } -// NewRateLimiter returns limiter with limit per 2 seconds -func NewRateLimiter(limiterName string, limit int64) *RateLimiter { +// Throttle registers reconcile event and checks if it must be throttled +func (rt *RateLimiter) Throttle() bool { + if rt.budget <= 0 { + if d := time.Until(rt.deadline); d > 0 { + return true + } + rt.deadline = time.Now().Add(rt.duration) + rt.budget += rt.limit + } + rt.budget-- + return false +} + +// NewReconcileRateLimiter returns limiter with limit per 2 seconds +func NewReconcileRateLimiter(limiterName string, limit int64) *ReconcileRateLimiter { collector := prometheus.NewCounter(prometheus.CounterOpts{ Name: "operator_reconcile_throttled_events_total", Help: "number of throttled reconciliation events", ConstLabels: map[string]string{"controller": limiterName}, }) - r := metrics.Registry - r.MustRegister(collector) - return &RateLimiter{ - limit: limit, + metrics.Registry.MustRegister(collector) + return &ReconcileRateLimiter{ + RateLimiter: RateLimiter{ + limit: limit, + duration: time.Second * 2, + }, throttled: collector, } } -// MustThrottleReconcile registers reconcile event and checks if it must be throttled -func (rt *RateLimiter) MustThrottleReconcile() bool { +// ReconcileRateLimiter limits reconcile callback calls +type ReconcileRateLimiter struct { + RateLimiter + mu sync.Mutex + throttled prometheus.Counter +} + +// Throttle registers reconcile event and checks if it must be throttled +func (rt *ReconcileRateLimiter) Throttle() bool { rt.mu.Lock() defer rt.mu.Unlock() - if rt.budget <= 0 { - if d := time.Until(rt.deadline); d > 0 { - rt.throttled.Inc() - return true - } - rt.deadline = time.Now().Add(time.Second * 2) - rt.budget += rt.limit + if rt.RateLimiter.Throttle() { + rt.throttled.Inc() + return true } - rt.budget-- return false } diff --git a/internal/controller/operator/factory/limiter/limiter_test.go b/internal/controller/operator/factory/limiter/limiter_test.go new file mode 100644 index 000000000..b4f32cc91 --- /dev/null +++ b/internal/controller/operator/factory/limiter/limiter_test.go @@ -0,0 +1,107 @@ +package limiter + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestRateLimiter_Throttle(t *testing.T) { + f := func(limit int64, duration time.Duration, calls int, wantThrottled int) { + t.Helper() + rl := NewRateLimiter(limit, duration) + throttled := 0 + for i := 0; i < calls; i++ { + if rl.Throttle() { + throttled++ + } + } + assert.Equal(t, wantThrottled, throttled) + } + + // happy path: 5 calls + f(5, time.Second, 5, 0) + + // happy path: one call + f(1, time.Second, 1, 0) + + // 3 calls over budget: 3 get throttled + f(3, time.Second, 6, 3) + + // zero budget: first call succeeds, rest throttled + f(0, time.Second, 3, 2) +} + +func TestRateLimiter_ThrottleResetsAfterDeadline(t *testing.T) { + rl := NewRateLimiter(2, 10*time.Millisecond) + + // exhaust the budget + assert.False(t, rl.Throttle()) + assert.False(t, rl.Throttle()) + // throttling begins + assert.True(t, rl.Throttle()) + + // budget resets after deadline + time.Sleep(20 * time.Millisecond) + assert.False(t, rl.Throttle()) + assert.False(t, rl.Throttle()) + // throttling begins + assert.True(t, rl.Throttle()) +} + +func TestReconcileRateLimiter_Throttle(t *testing.T) { + f := func(limit int64, calls int, wantThrottled int) { + t.Helper() + // create ReconcileRateLimiter directly to avoid prometheus registration + rl := &ReconcileRateLimiter{ + RateLimiter: RateLimiter{ + limit: limit, + duration: time.Second, + }, + } + throttled := 0 + for i := 0; i < calls; i++ { + rl.mu.Lock() + got := rl.RateLimiter.Throttle() + rl.mu.Unlock() + if got { + throttled++ + } + } + assert.Equal(t, wantThrottled, throttled) + } + + // happy path + f(5, 5, 0) + + // zero budget: first call succeeds, rest throttled + f(0, 3, 2) + + // partial throttle + f(2, 5, 3) +} + +func TestReconcileRateLimiter_Concurrent(t *testing.T) { + rl := &ReconcileRateLimiter{ + RateLimiter: RateLimiter{ + limit: 10, + duration: time.Second, + }, + } + + done := make(chan bool, 20) + for i := 0; i < 20; i++ { + go func() { + rl.mu.Lock() + rl.RateLimiter.Throttle() + rl.mu.Unlock() + done <- true + }() + } + for i := 0; i < 20; i++ { + <-done + } + // rate limited doesn't panic or race on negative budget + assert.LessOrEqual(t, rl.budget, int64(0)) +} diff --git a/internal/controller/operator/factory/logger/logger.go b/internal/controller/operator/factory/logger/logger.go index 2ab702a7c..4433f067d 100644 --- a/internal/controller/operator/factory/logger/logger.go +++ b/internal/controller/operator/factory/logger/logger.go @@ -92,6 +92,6 @@ func SelectedObjects(ctx context.Context, objectName string, selected, broken in if len(namespacedNames) < 250 { formattedNames = strings.Join(namespacedNames, ",") } - WithContext(ctx).Info(fmt.Sprintf("selected %s count=%d, invalid rules count=%d, namespaced names %s", - objectName, len(namespacedNames), broken, formattedNames)) + WithContext(ctx).V(1).Info(fmt.Sprintf("selected %s count=%d, invalid %s count=%d, namespaced names %s", + objectName, len(namespacedNames), objectName, broken, formattedNames)) } diff --git a/internal/controller/operator/factory/reconcile/reconcile.go b/internal/controller/operator/factory/reconcile/reconcile.go index c4deac688..ae03741c0 100644 --- a/internal/controller/operator/factory/reconcile/reconcile.go +++ b/internal/controller/operator/factory/reconcile/reconcile.go @@ -20,6 +20,7 @@ import ( vmv1beta1 "github.com/VictoriaMetrics/operator/api/operator/v1beta1" "github.com/VictoriaMetrics/operator/internal/config" "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/finalize" + "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/limiter" "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/logger" ) @@ -159,7 +160,7 @@ func waitForStatus[T client.Object, ST StatusWithMetadata[STC], STC any]( ) error { lastStatus := obj.GetStatusMetadata() nsn := types.NamespacedName{Name: obj.GetName(), Namespace: obj.GetNamespace()} - lastLogged := time.Now() + limiter := limiter.NewRateLimiter(1, vmWaitLogInterval) err := wait.PollUntilContextCancel(ctx, interval, false, func(ctx context.Context) (done bool, err error) { if err = rclient.Get(ctx, nsn, obj); err != nil { if k8serrors.IsNotFound(err) { @@ -169,10 +170,8 @@ func waitForStatus[T client.Object, ST StatusWithMetadata[STC], STC any]( return } lastStatus = obj.GetStatusMetadata() - - if lastStatus != nil && time.Now().After(lastLogged.Add(vmWaitLogInterval)) { + if lastStatus != nil && !limiter.Throttle() { logger.WithContext(ctx).V(1).Info(fmt.Sprintf("waiting for %T=%s to be ready, current status: %s", obj, nsn.String(), string(lastStatus.UpdateStatus))) - lastLogged = time.Now() } return lastStatus != nil && obj.GetGeneration() == lastStatus.ObservedGeneration && lastStatus.UpdateStatus == status, nil }) diff --git a/internal/controller/operator/factory/reconcile/statefulset.go b/internal/controller/operator/factory/reconcile/statefulset.go index ff61f4284..0323dfed5 100644 --- a/internal/controller/operator/factory/reconcile/statefulset.go +++ b/internal/controller/operator/factory/reconcile/statefulset.go @@ -244,7 +244,7 @@ func performRollingUpdateOnSts(ctx context.Context, rclient client.Client, obj * l.Info("sts has 0 replicas configured, nothing to update") return nil } - l.Info(fmt.Sprintf("check if pod update needed to desiredVersion=%s, podMustRecreate=%v", stsVersion, o.recreate)) + l.V(1).Info(fmt.Sprintf("check if pod update needed to desiredVersion=%s, podMustRecreate=%v", stsVersion, o.recreate)) var podList corev1.PodList opts := &client.ListOptions{ Namespace: obj.Namespace, diff --git a/internal/controller/operator/vmagent_controller.go b/internal/controller/operator/vmagent_controller.go index cfd3f8689..508c4e308 100644 --- a/internal/controller/operator/vmagent_controller.go +++ b/internal/controller/operator/vmagent_controller.go @@ -41,7 +41,7 @@ import ( var ( agentSync sync.RWMutex - agentReconcileLimit = limiter.NewRateLimiter("vmagent", 5) + agentReconcileLimit = limiter.NewReconcileRateLimiter("vmagent", 5) ) // VMAgentReconciler reconciles a VMAgent object @@ -153,7 +153,7 @@ func (*VMAgentReconciler) IsDisabled(_ *config.BaseOperatorConf, _ sets.Set[stri } func collectVMAgentScrapes(l logr.Logger, ctx context.Context, rclient client.Client, watchNamespaces []string, instance client.Object) (err error) { - if build.IsControllerDisabled("VMAgent") && agentReconcileLimit.MustThrottleReconcile() { + if build.IsControllerDisabled("VMAgent") && agentReconcileLimit.Throttle() { return nil } agentSync.Lock() diff --git a/internal/controller/operator/vmalert_controller.go b/internal/controller/operator/vmalert_controller.go index e3117d75a..1b05f3c7f 100644 --- a/internal/controller/operator/vmalert_controller.go +++ b/internal/controller/operator/vmalert_controller.go @@ -38,7 +38,7 @@ import ( var ( alertSync sync.RWMutex - alertReconcileLimit = limiter.NewRateLimiter("vmalert", 5) + alertReconcileLimit = limiter.NewReconcileRateLimiter("vmalert", 5) ) // VMAlertReconciler reconciles a VMAlert object diff --git a/internal/controller/operator/vmalertmanager_controller.go b/internal/controller/operator/vmalertmanager_controller.go index ae8595220..4b8f418cc 100644 --- a/internal/controller/operator/vmalertmanager_controller.go +++ b/internal/controller/operator/vmalertmanager_controller.go @@ -38,7 +38,7 @@ import ( var ( alertmanagerSync sync.RWMutex - alertmanagerReconcileLimit = limiter.NewRateLimiter("vmalertmanager", 5) + alertmanagerReconcileLimit = limiter.NewReconcileRateLimiter("vmalertmanager", 5) ) // VMAlertmanagerReconciler reconciles a VMAlertmanager object diff --git a/internal/controller/operator/vmalertmanagerconfig_controller.go b/internal/controller/operator/vmalertmanagerconfig_controller.go index 4b03558bf..215d600af 100644 --- a/internal/controller/operator/vmalertmanagerconfig_controller.go +++ b/internal/controller/operator/vmalertmanagerconfig_controller.go @@ -75,7 +75,7 @@ func (r *VMAlertmanagerConfigReconciler) Reconcile(ctx context.Context, req ctrl err = &parsingError{instance.Spec.ParsingError, "vmalertmanagerconfig"} return } - if alertmanagerReconcileLimit.MustThrottleReconcile() { + if alertmanagerReconcileLimit.Throttle() { return } diff --git a/internal/controller/operator/vmauth_controller.go b/internal/controller/operator/vmauth_controller.go index c8f47b891..0157a77ca 100644 --- a/internal/controller/operator/vmauth_controller.go +++ b/internal/controller/operator/vmauth_controller.go @@ -38,7 +38,7 @@ import ( var ( authSync sync.RWMutex - authReconcileLimit = limiter.NewRateLimiter("vmauth", 5) + authReconcileLimit = limiter.NewReconcileRateLimiter("vmauth", 5) ) // VMAuthReconciler reconciles a VMAuth object diff --git a/internal/controller/operator/vmrule_controller.go b/internal/controller/operator/vmrule_controller.go index 0bd25c6e5..7ab942896 100644 --- a/internal/controller/operator/vmrule_controller.go +++ b/internal/controller/operator/vmrule_controller.go @@ -78,7 +78,7 @@ func (r *VMRuleReconciler) Reconcile(ctx context.Context, req ctrl.Request) (res err = &parsingError{instance.Spec.ParsingError, "vmrule"} return } - if alertReconcileLimit.MustThrottleReconcile() { + if alertReconcileLimit.Throttle() { return } diff --git a/internal/controller/operator/vmuser_controller.go b/internal/controller/operator/vmuser_controller.go index 44965ca72..e54b7b2f4 100644 --- a/internal/controller/operator/vmuser_controller.go +++ b/internal/controller/operator/vmuser_controller.go @@ -89,7 +89,7 @@ func (r *VMUserReconciler) Reconcile(ctx context.Context, req ctrl.Request) (res return } - if authReconcileLimit.MustThrottleReconcile() { + if authReconcileLimit.Throttle() { return } From f8ae8da668c62580cc0157b5fe37271feccbe361 Mon Sep 17 00:00:00 2001 From: Andrii Chubatiuk Date: Tue, 31 Mar 2026 11:55:28 +0300 Subject: [PATCH 18/22] reconcile: do not swallow error that may happen if STS PVC update failed (#2013) Co-authored-by: Vadim Rutkovsky --- .../operator/factory/k8stools/interceptors.go | 19 +- .../operator/factory/k8stools/test_helpers.go | 13 +- .../operator/factory/reconcile/statefulset.go | 16 +- .../reconcile/statefulset_pvc_expand.go | 14 +- .../reconcile/statefulset_pvc_expand_test.go | 6 + .../factory/reconcile/statefulset_test.go | 120 ++++++++- .../reconcile_and_track_status_test.go | 244 ++++++++++++++++++ 7 files changed, 406 insertions(+), 26 deletions(-) create mode 100644 internal/controller/operator/reconcile_and_track_status_test.go diff --git a/internal/controller/operator/factory/k8stools/interceptors.go b/internal/controller/operator/factory/k8stools/interceptors.go index f8d719825..655867db1 100644 --- a/internal/controller/operator/factory/k8stools/interceptors.go +++ b/internal/controller/operator/factory/k8stools/interceptors.go @@ -13,7 +13,14 @@ import ( vmv1beta1 "github.com/VictoriaMetrics/operator/api/operator/v1beta1" ) -func updateStatus(ctx context.Context, cl client.WithWatch, obj client.Object) error { +type ClientOpts struct { + SkipPVCStatusUpdate bool +} + +func updateStatus(ctx context.Context, cl client.WithWatch, obj client.Object, o *ClientOpts) error { + if o == nil { + o = new(ClientOpts) + } switch v := obj.(type) { case *appsv1.StatefulSet: v.Status.ObservedGeneration = v.Generation @@ -41,7 +48,9 @@ func updateStatus(ctx context.Context, cl client.WithWatch, obj client.Object) e v.Status.UpdateStatus = vmv1beta1.UpdateStatusOperational v.Status.ObservedGeneration = v.Generation case *corev1.PersistentVolumeClaim: - v.Status.Capacity = v.Spec.Resources.Requests + if !o.SkipPVCStatusUpdate { + v.Status.Capacity = v.Spec.Resources.Requests + } default: return nil } @@ -49,19 +58,19 @@ func updateStatus(ctx context.Context, cl client.WithWatch, obj client.Object) e } // GetInterceptorsWithObjects returns interceptors for objects -func GetInterceptorsWithObjects() interceptor.Funcs { +func GetInterceptorsWithObjects(o *ClientOpts) interceptor.Funcs { return interceptor.Funcs{ Create: func(ctx context.Context, cl client.WithWatch, obj client.Object, opts ...client.CreateOption) error { if err := cl.Create(ctx, obj, opts...); err != nil { return err } - return updateStatus(ctx, cl, obj) + return updateStatus(ctx, cl, obj, o) }, Update: func(ctx context.Context, cl client.WithWatch, obj client.Object, opts ...client.UpdateOption) error { if err := cl.Update(ctx, obj, opts...); err != nil { return err } - return updateStatus(ctx, cl, obj) + return updateStatus(ctx, cl, obj, o) }, } } diff --git a/internal/controller/operator/factory/k8stools/test_helpers.go b/internal/controller/operator/factory/k8stools/test_helpers.go index 9a3643ae4..ef38b5d59 100644 --- a/internal/controller/operator/factory/k8stools/test_helpers.go +++ b/internal/controller/operator/factory/k8stools/test_helpers.go @@ -98,7 +98,7 @@ func GetTestClientWithObjectsAndInterceptors(predefinedObjects []runtime.Object, // GetTestClientWithObjects returns testing client with optional predefined objects func GetTestClientWithObjects(predefinedObjects []runtime.Object) client.Client { - fns := GetInterceptorsWithObjects() + fns := GetInterceptorsWithObjects(nil) return getTestClient(predefinedObjects, &fns) } @@ -163,7 +163,16 @@ func GetTestClientWithActions(predefinedObjects []runtime.Object) *ClientWithAct // GetTestClientWithActionsAndObjects returns a client that tracks actions and updates status/objects func GetTestClientWithActionsAndObjects(predefinedObjects []runtime.Object) *ClientWithActions { var cwa ClientWithActions - objectInterceptors := GetInterceptorsWithObjects() + objectInterceptors := GetInterceptorsWithObjects(nil) + + cwa.Client = GetTestClientWithObjectsAndInterceptors(predefinedObjects, NewActionRecordingInterceptor(&cwa.Actions, &objectInterceptors)) + return &cwa +} + +// GetTestClientWithOptsActionsAndObjects returns a client that tracks actions and updates status/objects +func GetTestClientWithOptsActionsAndObjects(predefinedObjects []runtime.Object, o *ClientOpts) *ClientWithActions { + var cwa ClientWithActions + objectInterceptors := GetInterceptorsWithObjects(o) cwa.Client = GetTestClientWithObjectsAndInterceptors(predefinedObjects, NewActionRecordingInterceptor(&cwa.Actions, &objectInterceptors)) return &cwa diff --git a/internal/controller/operator/factory/reconcile/statefulset.go b/internal/controller/operator/factory/reconcile/statefulset.go index 0323dfed5..f03a281ca 100644 --- a/internal/controller/operator/factory/reconcile/statefulset.go +++ b/internal/controller/operator/factory/reconcile/statefulset.go @@ -88,8 +88,8 @@ func StatefulSet(ctx context.Context, rclient client.Client, newObj, prevObj *ap if o == nil { o = new(StatefulSetOpts) } + var existingObj appsv1.StatefulSet err := retryOnConflict(func() error { - var existingObj appsv1.StatefulSet if err := rclient.Get(ctx, nsn, &existingObj); err != nil { if k8serrors.IsNotFound(err) { logger.WithContext(ctx).Info(fmt.Sprintf("creating new StatefulSet=%s", nsn.String())) @@ -124,13 +124,6 @@ func StatefulSet(ctx context.Context, rclient client.Client, newObj, prevObj *ap return newErrRecreate(ctx, &existingObj) } - // check if pvcs need to resize - if len(newObj.Spec.VolumeClaimTemplates) > 0 { - if err := updateSTSPVC(ctx, rclient, &existingObj, prevVCTs); err != nil { - return err - } - } - metaChanged, err := mergeMeta(&existingObj, newObj, prevMeta, owner, removeFinalizer) if err != nil { return err @@ -153,6 +146,13 @@ func StatefulSet(ctx context.Context, rclient client.Client, newObj, prevObj *ap return err } + // check if pvcs need to resize + if len(existingObj.Spec.VolumeClaimTemplates) > 0 { + if err := updateSTSPVC(ctx, rclient, newObj, prevVCTs); err != nil { + return err + } + } + // perform manual update only with OnDelete policy, which is default. switch updateStrategy { case appsv1.OnDeleteStatefulSetStrategyType: diff --git a/internal/controller/operator/factory/reconcile/statefulset_pvc_expand.go b/internal/controller/operator/factory/reconcile/statefulset_pvc_expand.go index d32781a00..5f4a52b76 100644 --- a/internal/controller/operator/factory/reconcile/statefulset_pvc_expand.go +++ b/internal/controller/operator/factory/reconcile/statefulset_pvc_expand.go @@ -117,12 +117,18 @@ func updateSTSPVC(ctx context.Context, rclient client.Client, sts *appsv1.Statef continue } prevVCT := getPVCByName(prevVCTs, stsClaimName) - // update PVC size and metadata if it's needed - if err := updatePVC(ctx, rclient, &pvc, &stsClaim, prevVCT, nil); err != nil { + nsnPvc := types.NamespacedName{Name: pvc.Name, Namespace: pvc.Namespace} + // update PVC size and metadata if it's needed, retrying on conflict + if err := retryOnConflict(func() error { + var currentPVC corev1.PersistentVolumeClaim + if err := rclient.Get(ctx, nsnPvc, ¤tPVC); err != nil { + return err + } + return updatePVC(ctx, rclient, ¤tPVC, &stsClaim, prevVCT, nil) + }); err != nil { return err } - nsnPvc := types.NamespacedName{Name: pvc.Name, Namespace: pvc.Namespace} - size := pvc.Spec.Resources.Requests[corev1.ResourceStorage] + size := stsClaim.Spec.Resources.Requests[corev1.ResourceStorage] if err := waitForPVCReady(ctx, rclient, nsnPvc, size); err != nil { return err } diff --git a/internal/controller/operator/factory/reconcile/statefulset_pvc_expand_test.go b/internal/controller/operator/factory/reconcile/statefulset_pvc_expand_test.go index 9198bb225..93b471200 100644 --- a/internal/controller/operator/factory/reconcile/statefulset_pvc_expand_test.go +++ b/internal/controller/operator/factory/reconcile/statefulset_pvc_expand_test.go @@ -392,6 +392,7 @@ func Test_updateSTSPVC(t *testing.T) { })) }, actions: []k8stools.ClientAction{ + {Verb: "Get", Kind: "PersistentVolumeClaim", Resource: pvc1NSN}, {Verb: "Update", Kind: "PersistentVolumeClaim", Resource: pvc1NSN}, {Verb: "Get", Kind: "PersistentVolumeClaim", Resource: pvc1NSN}, }, @@ -505,8 +506,10 @@ func Test_updateSTSPVC(t *testing.T) { })) }, actions: []k8stools.ClientAction{ + {Verb: "Get", Kind: "PersistentVolumeClaim", Resource: pvc2NSN}, {Verb: "Update", Kind: "PersistentVolumeClaim", Resource: pvc2NSN}, {Verb: "Get", Kind: "PersistentVolumeClaim", Resource: pvc2NSN}, + {Verb: "Get", Kind: "PersistentVolumeClaim", Resource: pvc1NSN}, {Verb: "Update", Kind: "PersistentVolumeClaim", Resource: pvc1NSN}, {Verb: "Get", Kind: "PersistentVolumeClaim", Resource: pvc1NSN}, }, @@ -625,6 +628,7 @@ func Test_updateSTSPVC(t *testing.T) { }, }, }, + wantErr: true, }) // expand with annotation on non-expandable sc @@ -675,6 +679,7 @@ func Test_updateSTSPVC(t *testing.T) { })) }, actions: []k8stools.ClientAction{ + {Verb: "Get", Kind: "PersistentVolumeClaim", Resource: pvc1NSN}, {Verb: "Update", Kind: "PersistentVolumeClaim", Resource: pvc1NSN}, {Verb: "Get", Kind: "PersistentVolumeClaim", Resource: pvc1NSN}, }, @@ -763,6 +768,7 @@ func Test_updateSTSPVC(t *testing.T) { })) }, actions: []k8stools.ClientAction{ + {Verb: "Get", Kind: "PersistentVolumeClaim", Resource: pvc1NSN}, {Verb: "Update", Kind: "PersistentVolumeClaim", Resource: pvc1NSN}, {Verb: "Get", Kind: "PersistentVolumeClaim", Resource: pvc1NSN}, }, diff --git a/internal/controller/operator/factory/reconcile/statefulset_test.go b/internal/controller/operator/factory/reconcile/statefulset_test.go index 4a4ae9c55..33e89da5b 100644 --- a/internal/controller/operator/factory/reconcile/statefulset_test.go +++ b/internal/controller/operator/factory/reconcile/statefulset_test.go @@ -827,12 +827,13 @@ func TestSortPodsByID(t *testing.T) { func TestStatefulsetReconcile(t *testing.T) { type opts struct { - new, prev *appsv1.StatefulSet - predefinedObjects []runtime.Object - validate func(*appsv1.StatefulSet) - actions []k8stools.ClientAction - wantErr bool - o *StatefulSetOpts + new, prev *appsv1.StatefulSet + predefinedObjects []runtime.Object + validate func(*appsv1.StatefulSet) + actions []k8stools.ClientAction + skipPVCStatusUpdate bool + wantErr bool + o *StatefulSetOpts } getSts := func(fns ...func(s *appsv1.StatefulSet)) *appsv1.StatefulSet { s := &appsv1.StatefulSet{ @@ -879,7 +880,9 @@ func TestStatefulsetReconcile(t *testing.T) { f := func(o opts) { t.Helper() ctx := context.Background() - cl := k8stools.GetTestClientWithActionsAndObjects(o.predefinedObjects) + cl := k8stools.GetTestClientWithOptsActionsAndObjects(o.predefinedObjects, &k8stools.ClientOpts{ + SkipPVCStatusUpdate: o.skipPVCStatusUpdate, + }) synctest.Test(t, func(t *testing.T) { err := StatefulSet(ctx, cl, o.new, o.prev, nil, o.o) if o.wantErr { @@ -902,6 +905,7 @@ func TestStatefulsetReconcile(t *testing.T) { } nn := types.NamespacedName{Name: "test-1", Namespace: "default"} + pnn := types.NamespacedName{Name: "vmselect-cachedir-test-1-0", Namespace: "default"} // create statefulset f(opts{ @@ -1023,6 +1027,20 @@ func TestStatefulsetReconcile(t *testing.T) { }} }), predefinedObjects: []runtime.Object{ + &corev1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + Name: "vmselect-cachedir-test-1-0", + Namespace: "default", + Labels: map[string]string{"label": "value"}, + }, + Spec: corev1.PersistentVolumeClaimSpec{ + Resources: corev1.VolumeResourceRequirements{ + Requests: map[corev1.ResourceName]resource.Quantity{ + corev1.ResourceStorage: resource.MustParse("5Gi"), + }, + }, + }, + }, getSts(func(s *appsv1.StatefulSet) { s.Finalizers = []string{vmv1beta1.FinalizerName} s.Spec.VolumeClaimTemplates = []corev1.PersistentVolumeClaim{{ @@ -1053,10 +1071,98 @@ func TestStatefulsetReconcile(t *testing.T) { {Verb: "Delete", Kind: "StatefulSet", Resource: nn}, {Verb: "Get", Kind: "StatefulSet", Resource: nn}, {Verb: "Create", Kind: "StatefulSet", Resource: nn}, + {Verb: "Get", Kind: "PersistentVolumeClaim", Resource: pnn}, + {Verb: "Update", Kind: "PersistentVolumeClaim", Resource: pnn}, + {Verb: "Get", Kind: "PersistentVolumeClaim", Resource: pnn}, {Verb: "Get", Kind: "StatefulSet", Resource: nn}, }, }) + // fail while waiting for PVC expansion + f(opts{ + skipPVCStatusUpdate: true, + new: getSts(func(s *appsv1.StatefulSet) { + s.Spec.VolumeClaimTemplates = []corev1.PersistentVolumeClaim{{ + ObjectMeta: metav1.ObjectMeta{ + Name: "vmselect-cachedir", + Annotations: map[string]string{ + "operator.victoriametrics.com/pvc-allow-volume-expansion": "true", + "test": "test", + }, + Labels: map[string]string{"app": "vmselect"}, + }, + Spec: corev1.PersistentVolumeClaimSpec{ + Resources: corev1.VolumeResourceRequirements{ + Requests: map[corev1.ResourceName]resource.Quantity{ + corev1.ResourceStorage: resource.MustParse("10Gi"), + }, + }, + }, + }} + }), + prev: getSts(func(s *appsv1.StatefulSet) { + s.Spec.VolumeClaimTemplates = []corev1.PersistentVolumeClaim{{ + ObjectMeta: metav1.ObjectMeta{ + Name: "vmselect-cachedir", + Annotations: map[string]string{ + "operator.victoriametrics.com/pvc-allow-volume-expansion": "true", + "test": "test", + }, + Labels: map[string]string{"app": "vmselect"}, + }, + Spec: corev1.PersistentVolumeClaimSpec{ + Resources: corev1.VolumeResourceRequirements{ + Requests: map[corev1.ResourceName]resource.Quantity{ + corev1.ResourceStorage: resource.MustParse("5Gi"), + }, + }, + }, + }} + }), + predefinedObjects: []runtime.Object{ + &corev1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + Name: "vmselect-cachedir-test-1-0", + Namespace: "default", + Labels: map[string]string{"label": "value"}, + }, + Spec: corev1.PersistentVolumeClaimSpec{ + Resources: corev1.VolumeResourceRequirements{ + Requests: map[corev1.ResourceName]resource.Quantity{ + corev1.ResourceStorage: resource.MustParse("5Gi"), + }, + }, + }, + Status: corev1.PersistentVolumeClaimStatus{ + Capacity: map[corev1.ResourceName]resource.Quantity{ + corev1.ResourceStorage: resource.MustParse("4Gi"), + }, + }, + }, + getSts(func(s *appsv1.StatefulSet) { + s.Finalizers = []string{vmv1beta1.FinalizerName} + s.Spec.VolumeClaimTemplates = []corev1.PersistentVolumeClaim{{ + ObjectMeta: metav1.ObjectMeta{ + Name: "vmselect-cachedir", + Annotations: map[string]string{ + "operator.victoriametrics.com/pvc-allow-volume-expansion": "true", + "test": "test", + }, + Labels: map[string]string{"app": "vmselect"}, + }, + Spec: corev1.PersistentVolumeClaimSpec{ + Resources: corev1.VolumeResourceRequirements{ + Requests: map[corev1.ResourceName]resource.Quantity{ + corev1.ResourceStorage: resource.MustParse("5Gi"), + }, + }, + }, + }} + }), + }, + wantErr: true, + }) + // context deadline error f(opts{ new: getSts(func(s *appsv1.StatefulSet) { diff --git a/internal/controller/operator/reconcile_and_track_status_test.go b/internal/controller/operator/reconcile_and_track_status_test.go new file mode 100644 index 000000000..20e693cd1 --- /dev/null +++ b/internal/controller/operator/reconcile_and_track_status_test.go @@ -0,0 +1,244 @@ +package operator + +import ( + "context" + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/wait" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + + vmv1beta1 "github.com/VictoriaMetrics/operator/api/operator/v1beta1" + "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/k8stools" +) + +func TestReconcileAndTrackStatus(t *testing.T) { + nsn := types.NamespacedName{Name: "test-vmalert", Namespace: "default"} + + type opts struct { + object *vmv1beta1.VMAlert + cb func(client.Client) (ctrl.Result, error) + wantStatus vmv1beta1.UpdateStatus + wantResult ctrl.Result + wantErr bool + } + + f := func(o opts) { + t.Helper() + fclient := k8stools.GetTestClientWithObjects([]runtime.Object{o.object}) + result, err := reconcileAndTrackStatus(context.Background(), fclient, o.object, func() (ctrl.Result, error) { + return o.cb(fclient) + }) + if o.wantErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + assert.Equal(t, o.wantResult, result) + + got := &vmv1beta1.VMAlert{} + assert.NoError(t, fclient.Get(context.Background(), nsn, got)) + assert.Equal(t, o.wantStatus, got.Status.UpdateStatus) + } + + noop := func(_ client.Client) (ctrl.Result, error) { return ctrl.Result{}, nil } + + // object created (no prior status): callback succeeds → operational + f(opts{ + object: &vmv1beta1.VMAlert{ + ObjectMeta: metav1.ObjectMeta{Name: "test-vmalert", Namespace: "default"}, + Spec: vmv1beta1.VMAlertSpec{SelectAllByDefault: true}, + }, + cb: noop, + wantStatus: vmv1beta1.UpdateStatusOperational, + }) + + // spec unchanged: callback succeeds → operational + unchangedSpec := vmv1beta1.VMAlertSpec{SelectAllByDefault: true} + f(opts{ + object: &vmv1beta1.VMAlert{ + ObjectMeta: metav1.ObjectMeta{Name: "test-vmalert", Namespace: "default"}, + Spec: unchangedSpec, + ParsedLastAppliedSpec: unchangedSpec.DeepCopy(), + }, + cb: noop, + wantStatus: vmv1beta1.UpdateStatusOperational, + }) + + // retryable conflict error, operational → expanding + opSpec := vmv1beta1.VMAlertSpec{SelectAllByDefault: true} + f(opts{ + object: &vmv1beta1.VMAlert{ + ObjectMeta: metav1.ObjectMeta{Name: "test-vmalert", Namespace: "default"}, + Spec: opSpec, + ParsedLastAppliedSpec: opSpec.DeepCopy(), + Status: vmv1beta1.VMAlertStatus{ + StatusMetadata: vmv1beta1.StatusMetadata{UpdateStatus: vmv1beta1.UpdateStatusOperational}, + }, + }, + cb: func(_ client.Client) (ctrl.Result, error) { + return ctrl.Result{}, k8serrors.NewConflict(schema.GroupResource{Group: "apps", Resource: "deployments"}, "test", fmt.Errorf("conflict")) + }, + wantStatus: vmv1beta1.UpdateStatusExpanding, + }) + + // retryable wait interrupted, operational → expanding + f(opts{ + object: &vmv1beta1.VMAlert{ + ObjectMeta: metav1.ObjectMeta{Name: "test-vmalert", Namespace: "default"}, + Spec: opSpec, + ParsedLastAppliedSpec: opSpec.DeepCopy(), + Status: vmv1beta1.VMAlertStatus{ + StatusMetadata: vmv1beta1.StatusMetadata{UpdateStatus: vmv1beta1.UpdateStatusOperational}, + }, + }, + cb: func(_ client.Client) (ctrl.Result, error) { + return ctrl.Result{}, wait.ErrorInterrupted(fmt.Errorf("timeout")) + }, + wantStatus: vmv1beta1.UpdateStatusExpanding, + }) + + // operational → expanding → operational + prevSpec := vmv1beta1.VMAlertSpec{SelectAllByDefault: true} + newSpec := vmv1beta1.VMAlertSpec{SelectAllByDefault: false} + f(opts{ + object: &vmv1beta1.VMAlert{ + ObjectMeta: metav1.ObjectMeta{Name: "test-vmalert", Namespace: "default"}, + Spec: newSpec, + ParsedLastAppliedSpec: prevSpec.DeepCopy(), + Status: vmv1beta1.VMAlertStatus{ + StatusMetadata: vmv1beta1.StatusMetadata{UpdateStatus: vmv1beta1.UpdateStatusOperational}, + }, + }, + cb: func(c client.Client) (ctrl.Result, error) { + got := &vmv1beta1.VMAlert{} + assert.NoError(t, c.Get(context.Background(), nsn, got)) + assert.Equal(t, vmv1beta1.UpdateStatusExpanding, got.Status.UpdateStatus) + return ctrl.Result{}, nil + }, + wantStatus: vmv1beta1.UpdateStatusOperational, + }) + + pausedSpec := vmv1beta1.VMAlertSpec{ + SelectAllByDefault: true, + CommonAppsParams: vmv1beta1.CommonAppsParams{Paused: true}, + } + // object created as paused: callback not called, status set to paused + f(opts{ + object: &vmv1beta1.VMAlert{ + ObjectMeta: metav1.ObjectMeta{Name: "test-vmalert", Namespace: "default"}, + Spec: pausedSpec, + }, + cb: func(_ client.Client) (ctrl.Result, error) { + t.Fatal("callback must not be called when object is paused") + return ctrl.Result{}, nil + }, + wantStatus: vmv1beta1.UpdateStatusPaused, + }) + + // operational → paused + f(opts{ + object: &vmv1beta1.VMAlert{ + ObjectMeta: metav1.ObjectMeta{Name: "test-vmalert", Namespace: "default"}, + Spec: pausedSpec, + ParsedLastAppliedSpec: pausedSpec.DeepCopy(), + Status: vmv1beta1.VMAlertStatus{ + StatusMetadata: vmv1beta1.StatusMetadata{UpdateStatus: vmv1beta1.UpdateStatusOperational}, + }, + }, + cb: func(_ client.Client) (ctrl.Result, error) { + t.Fatal("callback must not be called when object is paused") + return ctrl.Result{}, nil + }, + wantStatus: vmv1beta1.UpdateStatusPaused, + }) + + // paused → operational + unpausedSpec := vmv1beta1.VMAlertSpec{SelectAllByDefault: true} + f(opts{ + object: &vmv1beta1.VMAlert{ + ObjectMeta: metav1.ObjectMeta{Name: "test-vmalert", Namespace: "default"}, + Spec: unpausedSpec, + ParsedLastAppliedSpec: pausedSpec.DeepCopy(), + Status: vmv1beta1.VMAlertStatus{ + StatusMetadata: vmv1beta1.StatusMetadata{UpdateStatus: vmv1beta1.UpdateStatusPaused}, + }, + }, + cb: noop, + wantStatus: vmv1beta1.UpdateStatusOperational, + }) + + // expanding → operational + expandingSpec := vmv1beta1.VMAlertSpec{SelectAllByDefault: true} + f(opts{ + object: &vmv1beta1.VMAlert{ + ObjectMeta: metav1.ObjectMeta{Name: "test-vmalert", Namespace: "default"}, + Spec: expandingSpec, + ParsedLastAppliedSpec: expandingSpec.DeepCopy(), + Status: vmv1beta1.VMAlertStatus{ + StatusMetadata: vmv1beta1.StatusMetadata{UpdateStatus: vmv1beta1.UpdateStatusExpanding}, + }, + }, + cb: noop, + wantStatus: vmv1beta1.UpdateStatusOperational, + }) + + // non-retryable error: status set to failed, error returned + f(opts{ + object: &vmv1beta1.VMAlert{ + ObjectMeta: metav1.ObjectMeta{Name: "test-vmalert", Namespace: "default"}, + }, + cb: func(_ client.Client) (ctrl.Result, error) { + return ctrl.Result{}, fmt.Errorf("some reconciliation error") + }, + wantStatus: vmv1beta1.UpdateStatusFailed, + wantErr: true, + }) + + // propagate custom result on success + f(opts{ + object: &vmv1beta1.VMAlert{ + ObjectMeta: metav1.ObjectMeta{Name: "test-vmalert", Namespace: "default"}, + }, + cb: func(_ client.Client) (ctrl.Result, error) { + return ctrl.Result{Requeue: true}, nil + }, + wantStatus: vmv1beta1.UpdateStatusOperational, + wantResult: ctrl.Result{Requeue: true}, + }) +} + +// TestVMClusterRemainsExpandingDuringPVCResize verifies that a VMCluster stays +// in Expanding status when PVC resize is still in progress (waitForPVCReady +// returns a retryable wait.Interrupted error). +func TestVMClusterRemainsExpandingDuringPVCResize(t *testing.T) { + clusterSpec := vmv1beta1.VMClusterSpec{RetentionPeriod: "1d"} + cluster := &vmv1beta1.VMCluster{ + ObjectMeta: metav1.ObjectMeta{Name: "test-vmcluster", Namespace: "default"}, + Spec: clusterSpec, + ParsedLastAppliedSpec: clusterSpec.DeepCopy(), + Status: vmv1beta1.VMClusterStatus{ + StatusMetadata: vmv1beta1.StatusMetadata{UpdateStatus: vmv1beta1.UpdateStatusOperational}, + }, + } + + fclient := k8stools.GetTestClientWithObjects([]runtime.Object{cluster}) + + // Simulate retryable PVC expand error + _, err := reconcileAndTrackStatus(context.Background(), fclient, cluster, func() (ctrl.Result, error) { + return ctrl.Result{}, wait.ErrorInterrupted(fmt.Errorf("pvc resize still in progress")) + }) + assert.NoError(t, err) + + got := &vmv1beta1.VMCluster{} + assert.NoError(t, fclient.Get(context.Background(), types.NamespacedName{Name: "test-vmcluster", Namespace: "default"}, got)) + assert.Equal(t, vmv1beta1.UpdateStatusExpanding, got.Status.UpdateStatus, + "VMCluster must remain in Expanding while PVC resize is in progress") +} From d4100302b76eae27879cc2fd3d44dba141aed8a4 Mon Sep 17 00:00:00 2001 From: Andrii Chubatiuk Date: Tue, 31 Mar 2026 12:12:46 +0300 Subject: [PATCH 19/22] minor logs update --- internal/controller/operator/factory/reconcile/statefulset.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/controller/operator/factory/reconcile/statefulset.go b/internal/controller/operator/factory/reconcile/statefulset.go index f03a281ca..c46c26e8b 100644 --- a/internal/controller/operator/factory/reconcile/statefulset.go +++ b/internal/controller/operator/factory/reconcile/statefulset.go @@ -280,7 +280,7 @@ func performRollingUpdateOnSts(ctx context.Context, rclient client.Client, obj * updatedNeeded := len(podsForUpdate) != 0 || len(updatedPods) != 0 if !updatedNeeded { - l.Info("no pod needs to be updated") + l.V(1).Info("no pod needs to be updated") return nil } From 1b272bd4a64b8fd6f9c11ad91854d1f4d24cfd69 Mon Sep 17 00:00:00 2001 From: Andrii Chubatiuk Date: Tue, 31 Mar 2026 15:44:44 +0300 Subject: [PATCH 20/22] vmdistributed: filter vmauth targets by owner reference (#2010) --- .../operator/factory/vmdistributed/vmauth.go | 23 ++++++--- .../vmdistributed/vmdistributed_test.go | 48 ++++++++++--------- 2 files changed, 42 insertions(+), 29 deletions(-) diff --git a/internal/controller/operator/factory/vmdistributed/vmauth.go b/internal/controller/operator/factory/vmdistributed/vmauth.go index da9050ecb..fab27ca50 100644 --- a/internal/controller/operator/factory/vmdistributed/vmauth.go +++ b/internal/controller/operator/factory/vmdistributed/vmauth.go @@ -11,14 +11,24 @@ import ( vmv1beta1 "github.com/VictoriaMetrics/operator/api/operator/v1beta1" ) -func vmClusterTargetRef(vmClusters []*vmv1beta1.VMCluster, excludeIds ...int) vmv1beta1.TargetRef { +func hasOwnerReference(owners []metav1.OwnerReference, owner *metav1.OwnerReference) bool { + for i := range owners { + o := &owners[i] + if o.APIVersion == owner.APIVersion && o.Kind == owner.Kind && o.Name == owner.Name { + return true + } + } + return false +} + +func vmClusterTargetRef(vmClusters []*vmv1beta1.VMCluster, owner *metav1.OwnerReference, excludeIds ...int) vmv1beta1.TargetRef { var urls []string for i := range vmClusters { if slices.Contains(excludeIds, i) { continue } vmCluster := vmClusters[i] - if vmCluster.CreationTimestamp.IsZero() { + if vmCluster.CreationTimestamp.IsZero() || !hasOwnerReference(vmCluster.OwnerReferences, owner) { continue } urls = append(urls, vmCluster.AsURL(vmv1beta1.ClusterComponentSelect)) @@ -36,14 +46,14 @@ func vmClusterTargetRef(vmClusters []*vmv1beta1.VMCluster, excludeIds ...int) vm } } -func vmAgentTargetRef(vmAgents []*vmv1beta1.VMAgent, excludeIds ...int) vmv1beta1.TargetRef { +func vmAgentTargetRef(vmAgents []*vmv1beta1.VMAgent, owner *metav1.OwnerReference, excludeIds ...int) vmv1beta1.TargetRef { var urls []string for i := range vmAgents { if slices.Contains(excludeIds, i) { continue } vmAgent := vmAgents[i] - if vmAgent.CreationTimestamp.IsZero() { + if vmAgent.CreationTimestamp.IsZero() || !hasOwnerReference(vmAgent.OwnerReferences, owner) { continue } urls = append(urls, vmAgent.AsURL()) @@ -77,8 +87,9 @@ func buildVMAuthLB(cr *vmv1alpha1.VMDistributed, vmAgents []*vmv1beta1.VMAgent, vmAuth.Spec.UnauthorizedUserAccessSpec = &vmv1beta1.VMAuthUnauthorizedUserAccessSpec{} } var targetRefs []vmv1beta1.TargetRef - targetRefs = append(targetRefs, vmAgentTargetRef(vmAgents, excludeIds...)) - targetRefs = append(targetRefs, vmClusterTargetRef(vmClusters, excludeIds...)) + owner := cr.AsOwner() + targetRefs = append(targetRefs, vmAgentTargetRef(vmAgents, &owner, excludeIds...)) + targetRefs = append(targetRefs, vmClusterTargetRef(vmClusters, &owner, excludeIds...)) vmAuth.Spec.UnauthorizedUserAccessSpec.URLMap = nil vmAuth.Spec.UnauthorizedUserAccessSpec.URLPrefix = nil vmAuth.Spec.UnauthorizedUserAccessSpec.TargetRefs = targetRefs diff --git a/internal/controller/operator/factory/vmdistributed/vmdistributed_test.go b/internal/controller/operator/factory/vmdistributed/vmdistributed_test.go index 9de06f196..73ae506ad 100644 --- a/internal/controller/operator/factory/vmdistributed/vmdistributed_test.go +++ b/internal/controller/operator/factory/vmdistributed/vmdistributed_test.go @@ -20,12 +20,13 @@ import ( "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/reconcile" ) -func newVMAgent(name, namespace string) *vmv1beta1.VMAgent { +func newVMAgent(name, namespace string, owner metav1.OwnerReference) *vmv1beta1.VMAgent { return &vmv1beta1.VMAgent{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: namespace, CreationTimestamp: metav1.Now(), + OwnerReferences: []metav1.OwnerReference{owner}, }, Spec: vmv1beta1.VMAgentSpec{ CommonAppsParams: vmv1beta1.CommonAppsParams{ @@ -35,13 +36,14 @@ func newVMAgent(name, namespace string) *vmv1beta1.VMAgent { } } -func newVMCluster(name, namespace, version string) *vmv1beta1.VMCluster { +func newVMCluster(name, namespace, version string, owner metav1.OwnerReference) *vmv1beta1.VMCluster { return &vmv1beta1.VMCluster{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: namespace, Labels: map[string]string{"tenant": "default"}, CreationTimestamp: metav1.Now(), + OwnerReferences: []metav1.OwnerReference{owner}, }, Spec: vmv1beta1.VMClusterSpec{ ClusterVersion: version, @@ -84,26 +86,6 @@ func beforeEach(o opts) *testData { vmagents: make([]*vmv1beta1.VMAgent, 0, zonesCount), } namespace := "default" - dzs := make([]vmv1alpha1.VMDistributedZone, zonesCount) - var predefinedObjects []runtime.Object - for i := range dzs { - name := fmt.Sprintf("vmcluster-%d", i+1) - vmCluster := newVMCluster(name, namespace, "v1.0.0") - vmAgent := newVMAgent(name, namespace) - zs.vmclusters = append(zs.vmclusters, vmCluster) - zs.vmagents = append(zs.vmagents, vmAgent) - predefinedObjects = append(predefinedObjects, vmAgent, vmCluster) - dzs[i] = vmv1alpha1.VMDistributedZone{ - Name: name, - VMCluster: vmv1alpha1.VMDistributedZoneCluster{ - Name: name, - Spec: vmCluster.Spec, - }, - VMAgent: vmv1alpha1.VMDistributedZoneAgent{ - Name: name, - }, - } - } cr := &vmv1alpha1.VMDistributed{ TypeMeta: metav1.TypeMeta{ Kind: "VMDistributed", @@ -114,12 +96,32 @@ func beforeEach(o opts) *testData { Namespace: namespace, }, Spec: vmv1alpha1.VMDistributedSpec{ - Zones: dzs, + Zones: make([]vmv1alpha1.VMDistributedZone, zonesCount), VMAuth: vmv1alpha1.VMDistributedAuth{ Name: "vmauth-proxy", }, }, } + var predefinedObjects []runtime.Object + owner := cr.AsOwner() + for i := range cr.Spec.Zones { + name := fmt.Sprintf("vmcluster-%d", i+1) + vmCluster := newVMCluster(name, namespace, "v1.0.0", owner) + vmAgent := newVMAgent(name, namespace, owner) + zs.vmclusters = append(zs.vmclusters, vmCluster) + zs.vmagents = append(zs.vmagents, vmAgent) + predefinedObjects = append(predefinedObjects, vmAgent, vmCluster) + cr.Spec.Zones[i] = vmv1alpha1.VMDistributedZone{ + Name: name, + VMCluster: vmv1alpha1.VMDistributedZoneCluster{ + Name: name, + Spec: vmCluster.Spec, + }, + VMAgent: vmv1alpha1.VMDistributedZoneAgent{ + Name: name, + }, + } + } predefinedObjects = append(predefinedObjects, cr) d := &testData{ From 025dffa24f71f59b5ccb2b91210e1e8ec44d967c Mon Sep 17 00:00:00 2001 From: Andrii Chubatiuk Date: Mon, 30 Mar 2026 11:04:19 +0300 Subject: [PATCH 21/22] vlcluster,vtcluster: do not ignore extraStorageNode, when default storage is not enabled (#1911) Co-authored-by: Vadim Rutkovsky --- api/operator/v1/vlcluster_types.go | 53 ++++++++++++------ api/operator/v1/vtcluster_types.go | 54 +++++++++++++------ docs/CHANGELOG.md | 1 + .../operator/factory/vlcluster/vlselect.go | 18 ++++--- .../operator/factory/vtcluster/select.go | 18 ++++--- 5 files changed, 94 insertions(+), 50 deletions(-) diff --git a/api/operator/v1/vlcluster_types.go b/api/operator/v1/vlcluster_types.go index 9b13feb5d..903903c69 100644 --- a/api/operator/v1/vlcluster_types.go +++ b/api/operator/v1/vlcluster_types.go @@ -25,6 +25,7 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/util/sets" "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client" @@ -696,23 +697,6 @@ func (cr *VLCluster) Validate() error { if vmv1beta1.MustSkipCRValidation(cr) { return nil } - if cr.Spec.VLSelect != nil { - vms := cr.Spec.VLSelect - name := cr.PrefixedName(vmv1beta1.ClusterComponentSelect) - if vms.ServiceSpec != nil && vms.ServiceSpec.Name == name { - return fmt.Errorf(".serviceSpec.Name cannot be equal to prefixed name=%q", name) - } - if vms.HPA != nil { - if err := vms.HPA.Validate(); err != nil { - return err - } - } - if vms.VPA != nil { - if err := vms.VPA.Validate(); err != nil { - return err - } - } - } if cr.Spec.VLInsert != nil { vli := cr.Spec.VLInsert name := cr.PrefixedName(vmv1beta1.ClusterComponentInsert) @@ -730,6 +714,7 @@ func (cr *VLCluster) Validate() error { } } } + storageNodes := sets.New[string]() if cr.Spec.VLStorage != nil { vls := cr.Spec.VLStorage name := cr.PrefixedName(vmv1beta1.ClusterComponentStorage) @@ -745,6 +730,40 @@ func (cr *VLCluster) Validate() error { } } } + if cr.Spec.VLSelect != nil { + vms := cr.Spec.VLSelect + name := cr.PrefixedName(vmv1beta1.ClusterComponentSelect) + if vms.ServiceSpec != nil && vms.ServiceSpec.Name == name { + return fmt.Errorf(".serviceSpec.Name cannot be equal to prefixed name=%q", name) + } + if vms.HPA != nil { + if err := vms.HPA.Validate(); err != nil { + return err + } + } + if vms.VPA != nil { + if err := vms.VPA.Validate(); err != nil { + return err + } + } + if nodes, ok := cr.Spec.VLSelect.ExtraArgs["storageNode"]; ok { + for _, node := range strings.Split(nodes, ",") { + node = strings.TrimSpace(node) + if storageNodes.Has(node) { + return fmt.Errorf("encountered storageNode=%s multiple times, please make all storage node addresses are unique", node) + } else { + storageNodes.Insert(node) + } + } + } + for _, node := range cr.Spec.VLSelect.ExtraStorageNodes { + if storageNodes.Has(node.Addr) { + return fmt.Errorf("encountered storageNode=%s multiple times, please make all storage node addresses are unique", node.Addr) + } else { + storageNodes.Insert(node.Addr) + } + } + } if cr.Spec.RequestsLoadBalancer.Enabled { rlb := cr.Spec.RequestsLoadBalancer.Spec name := cr.PrefixedName(vmv1beta1.ClusterComponentBalancer) diff --git a/api/operator/v1/vtcluster_types.go b/api/operator/v1/vtcluster_types.go index 9c5524e7c..8a8c9121f 100644 --- a/api/operator/v1/vtcluster_types.go +++ b/api/operator/v1/vtcluster_types.go @@ -25,6 +25,7 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/util/sets" "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client" @@ -606,23 +607,6 @@ func (cr *VTCluster) Validate() error { if vmv1beta1.MustSkipCRValidation(cr) { return nil } - if cr.Spec.Select != nil { - vms := cr.Spec.Select - name := cr.PrefixedName(vmv1beta1.ClusterComponentSelect) - if vms.ServiceSpec != nil && vms.ServiceSpec.Name == name { - return fmt.Errorf(".serviceSpec.Name cannot be equal to prefixed name=%q", name) - } - if vms.HPA != nil { - if err := vms.HPA.Validate(); err != nil { - return err - } - } - if vms.VPA != nil { - if err := vms.VPA.Validate(); err != nil { - return err - } - } - } if cr.Spec.Insert != nil { vti := cr.Spec.Insert name := cr.PrefixedName(vmv1beta1.ClusterComponentInsert) @@ -640,7 +624,9 @@ func (cr *VTCluster) Validate() error { } } } + storageNodes := sets.New[string]() if cr.Spec.Storage != nil { + storageNodes.Insert(cr.AsURL(vmv1beta1.ClusterComponentStorage)) vts := cr.Spec.Storage name := cr.PrefixedName(vmv1beta1.ClusterComponentStorage) if vts.ServiceSpec != nil && vts.ServiceSpec.Name == name { @@ -655,6 +641,40 @@ func (cr *VTCluster) Validate() error { } } } + if cr.Spec.Select != nil { + vms := cr.Spec.Select + name := cr.PrefixedName(vmv1beta1.ClusterComponentSelect) + if vms.ServiceSpec != nil && vms.ServiceSpec.Name == name { + return fmt.Errorf(".serviceSpec.Name cannot be equal to prefixed name=%q", name) + } + if vms.HPA != nil { + if err := vms.HPA.Validate(); err != nil { + return err + } + } + if vms.VPA != nil { + if err := vms.VPA.Validate(); err != nil { + return err + } + } + if nodes, ok := cr.Spec.Select.ExtraArgs["storageNode"]; ok { + for _, node := range strings.Split(nodes, ",") { + node = strings.TrimSpace(node) + if storageNodes.Has(node) { + return fmt.Errorf("encountered storageNode=%s multiple times, please make all storage node addresses are unique", node) + } else { + storageNodes.Insert(node) + } + } + } + for _, node := range cr.Spec.Select.ExtraStorageNodes { + if storageNodes.Has(node.Addr) { + return fmt.Errorf("encountered storageNode=%s multiple times, please make all storage node addresses are unique", node.Addr) + } else { + storageNodes.Insert(node.Addr) + } + } + } if cr.Spec.RequestsLoadBalancer.Enabled { rlb := cr.Spec.RequestsLoadBalancer.Spec name := cr.PrefixedName(vmv1beta1.ClusterComponentBalancer) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 6f2a2f8a7..cabfdf4f7 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -19,6 +19,7 @@ aliases: * BUGFIX: [vmalertmanager](https://docs.victoriametrics.com/operator/resources/vmalertmanager/): fixed ignored tracing config, when no alertmanagerconfig CRs collected. See [#1983](https://github.com/VictoriaMetrics/operator/issues/1983). * BUGFIX: [vmagent](https://docs.victoriametrics.com/operator/resources/vmagent/): apply scrape class relabellings before job ones. See [#1997](https://github.com/VictoriaMetrics/operator/issues/1997). * BUGFIX: [vmanomaly](https://docs.victoriametrics.com/operator/resources/vmanomaly/) and [vmagent](https://docs.victoriametrics.com/operator/resources/vmagent/): render %SHARD_NUM% placeholder when shard count is greater than 0. See [#2001](https://github.com/VictoriaMetrics/operator/issues/2001). +* BUGFIX: [vlcluster](https://docs.victoriametrics.com/operator/resources/vlcluster/) and [vtcluster](https://docs.victoriametrics.com/operator/resources/vtcluster/): do not ignore ExtraStorageNodes for select, when default storage is disabled. See [#1910](https://github.com/VictoriaMetrics/operator/issues/1910). ## [v0.68.3](https://github.com/VictoriaMetrics/operator/releases/tag/v0.68.3) **Release date:** 16 March 2026 diff --git a/internal/controller/operator/factory/vlcluster/vlselect.go b/internal/controller/operator/factory/vlcluster/vlselect.go index c3f86d023..f7fd81211 100644 --- a/internal/controller/operator/factory/vlcluster/vlselect.go +++ b/internal/controller/operator/factory/vlcluster/vlselect.go @@ -240,20 +240,22 @@ func buildVLSelectPodSpec(cr *vmv1.VLCluster) (*corev1.PodTemplateSpec, error) { args = append(args, fmt.Sprintf("-loggerFormat=%s", cr.Spec.VLSelect.LogFormat)) } + storageNodeFlag := build.NewFlag("-storageNode", "") + storageNodeIds := cr.AvailableStorageNodeIDs("select") if cr.Spec.VLStorage != nil && cr.Spec.VLStorage.ReplicaCount != nil { // TODO: check TLS - storageNodeFlag := build.NewFlag("-storageNode", "") - storageNodeIds := cr.AvailableStorageNodeIDs("select") for idx, i := range storageNodeIds { storageNodeFlag.Add(build.PodDNSAddress(cr.PrefixedName(vmv1beta1.ClusterComponentStorage), i, cr.Namespace, cr.Spec.VLStorage.Port, cr.Spec.ClusterDomainName), idx) } - if len(cr.Spec.VLSelect.ExtraStorageNodes) > 0 { - for i, node := range cr.Spec.VLSelect.ExtraStorageNodes { - idx := i + len(storageNodeIds) - storageNodeFlag.Add(node.Addr, idx) - } + } + if len(cr.Spec.VLSelect.ExtraStorageNodes) > 0 { + for i, node := range cr.Spec.VLSelect.ExtraStorageNodes { + idx := i + len(storageNodeIds) + storageNodeFlag.Add(node.Addr, idx) } - totalNodes := len(cr.Spec.VLSelect.ExtraStorageNodes) + len(storageNodeIds) + } + totalNodes := len(cr.Spec.VLSelect.ExtraStorageNodes) + len(storageNodeIds) + if totalNodes > 0 { args = build.AppendFlagsToArgs(args, totalNodes, storageNodeFlag) } diff --git a/internal/controller/operator/factory/vtcluster/select.go b/internal/controller/operator/factory/vtcluster/select.go index 214bb69f5..2b2d9eb9b 100644 --- a/internal/controller/operator/factory/vtcluster/select.go +++ b/internal/controller/operator/factory/vtcluster/select.go @@ -239,20 +239,22 @@ func buildVTSelectPodSpec(cr *vmv1.VTCluster) (*corev1.PodTemplateSpec, error) { args = append(args, fmt.Sprintf("-loggerFormat=%s", cr.Spec.Select.LogFormat)) } + storageNodeFlag := build.NewFlag("-storageNode", "") + storageNodeIds := cr.AvailableStorageNodeIDs("select") if cr.Spec.Storage != nil && cr.Spec.Storage.ReplicaCount != nil { // TODO: check TLS - storageNodeFlag := build.NewFlag("-storageNode", "") - storageNodeIds := cr.AvailableStorageNodeIDs("select") for idx, i := range storageNodeIds { storageNodeFlag.Add(build.PodDNSAddress(cr.PrefixedName(vmv1beta1.ClusterComponentStorage), i, cr.Namespace, cr.Spec.Storage.Port, cr.Spec.ClusterDomainName), idx) } - if len(cr.Spec.Select.ExtraStorageNodes) > 0 { - for i, node := range cr.Spec.Select.ExtraStorageNodes { - idx := i + len(storageNodeIds) - storageNodeFlag.Add(node.Addr, idx) - } + } + if len(cr.Spec.Select.ExtraStorageNodes) > 0 { + for i, node := range cr.Spec.Select.ExtraStorageNodes { + idx := i + len(storageNodeIds) + storageNodeFlag.Add(node.Addr, idx) } - totalNodes := len(cr.Spec.Select.ExtraStorageNodes) + len(storageNodeIds) + } + totalNodes := len(cr.Spec.Select.ExtraStorageNodes) + len(storageNodeIds) + if totalNodes > 0 { args = build.AppendFlagsToArgs(args, totalNodes, storageNodeFlag) } From 14c05a0a1d49306813800e60b5890f9e96dfdfd8 Mon Sep 17 00:00:00 2001 From: Andrii Chubatiuk Date: Tue, 31 Mar 2026 19:43:39 +0300 Subject: [PATCH 22/22] followup for 1b272bd --- .../operator/factory/k8stools/interceptors.go | 17 +- .../operator/factory/reconcile/reconcile.go | 5 +- .../factory/reconcile/reconcile_test.go | 2 +- .../operator/factory/reconcile/vmagent.go | 6 +- .../operator/factory/reconcile/vmauth.go | 5 +- .../operator/factory/reconcile/vmcluster.go | 5 +- .../operator/factory/vmdistributed/vmauth.go | 11 +- .../vmdistributed_reconcile_test.go | 180 +++++++++++------- 8 files changed, 150 insertions(+), 81 deletions(-) diff --git a/internal/controller/operator/factory/k8stools/interceptors.go b/internal/controller/operator/factory/k8stools/interceptors.go index 655867db1..9d29b7a1e 100644 --- a/internal/controller/operator/factory/k8stools/interceptors.go +++ b/internal/controller/operator/factory/k8stools/interceptors.go @@ -5,6 +5,7 @@ import ( appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/apiutil" @@ -14,13 +15,11 @@ import ( ) type ClientOpts struct { - SkipPVCStatusUpdate bool + SkipPVCStatusUpdate bool + SetCreationTimestamp bool } func updateStatus(ctx context.Context, cl client.WithWatch, obj client.Object, o *ClientOpts) error { - if o == nil { - o = new(ClientOpts) - } switch v := obj.(type) { case *appsv1.StatefulSet: v.Status.ObservedGeneration = v.Generation @@ -59,14 +58,24 @@ func updateStatus(ctx context.Context, cl client.WithWatch, obj client.Object, o // GetInterceptorsWithObjects returns interceptors for objects func GetInterceptorsWithObjects(o *ClientOpts) interceptor.Funcs { + if o == nil { + o = new(ClientOpts) + } return interceptor.Funcs{ Create: func(ctx context.Context, cl client.WithWatch, obj client.Object, opts ...client.CreateOption) error { + if o.SetCreationTimestamp { + obj.SetCreationTimestamp(metav1.Now()) + } if err := cl.Create(ctx, obj, opts...); err != nil { return err } return updateStatus(ctx, cl, obj, o) }, Update: func(ctx context.Context, cl client.WithWatch, obj client.Object, opts ...client.UpdateOption) error { + ts := obj.GetCreationTimestamp() + if o.SetCreationTimestamp && ts.IsZero() { + obj.SetCreationTimestamp(metav1.Now()) + } if err := cl.Update(ctx, obj, opts...); err != nil { return err } diff --git a/internal/controller/operator/factory/reconcile/reconcile.go b/internal/controller/operator/factory/reconcile/reconcile.go index ae03741c0..ad575b4b9 100644 --- a/internal/controller/operator/factory/reconcile/reconcile.go +++ b/internal/controller/operator/factory/reconcile/reconcile.go @@ -157,11 +157,12 @@ func waitForStatus[T client.Object, ST StatusWithMetadata[STC], STC any]( obj ObjectWithDeepCopyAndStatus[T, ST, STC], interval time.Duration, status vmv1beta1.UpdateStatus, + minGeneration int64, ) error { lastStatus := obj.GetStatusMetadata() nsn := types.NamespacedName{Name: obj.GetName(), Namespace: obj.GetNamespace()} limiter := limiter.NewRateLimiter(1, vmWaitLogInterval) - err := wait.PollUntilContextCancel(ctx, interval, false, func(ctx context.Context) (done bool, err error) { + err := wait.PollUntilContextCancel(ctx, interval, true, func(ctx context.Context) (done bool, err error) { if err = rclient.Get(ctx, nsn, obj); err != nil { if k8serrors.IsNotFound(err) { return false, nil @@ -173,7 +174,7 @@ func waitForStatus[T client.Object, ST StatusWithMetadata[STC], STC any]( if lastStatus != nil && !limiter.Throttle() { logger.WithContext(ctx).V(1).Info(fmt.Sprintf("waiting for %T=%s to be ready, current status: %s", obj, nsn.String(), string(lastStatus.UpdateStatus))) } - return lastStatus != nil && obj.GetGeneration() == lastStatus.ObservedGeneration && lastStatus.UpdateStatus == status, nil + return lastStatus != nil && minGeneration <= lastStatus.ObservedGeneration && lastStatus.UpdateStatus == status, nil }) if err != nil { updateStatus := "unknown" diff --git a/internal/controller/operator/factory/reconcile/reconcile_test.go b/internal/controller/operator/factory/reconcile/reconcile_test.go index 810773488..17e8990db 100644 --- a/internal/controller/operator/factory/reconcile/reconcile_test.go +++ b/internal/controller/operator/factory/reconcile/reconcile_test.go @@ -35,7 +35,7 @@ func TestWaitForStatus(t *testing.T) { synctest.Test(t, func(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() - err := waitForStatus(ctx, rclient, vmc.DeepCopy(), 1*time.Second, vmv1beta1.UpdateStatusOperational) + err := waitForStatus(ctx, rclient, vmc.DeepCopy(), 1*time.Second, vmv1beta1.UpdateStatusOperational, 0) if isErr { assert.Error(t, err) } else { diff --git a/internal/controller/operator/factory/reconcile/vmagent.go b/internal/controller/operator/factory/reconcile/vmagent.go index 3acf1dd93..55fe37942 100644 --- a/internal/controller/operator/factory/reconcile/vmagent.go +++ b/internal/controller/operator/factory/reconcile/vmagent.go @@ -23,6 +23,8 @@ func VMAgent(ctx context.Context, rclient client.Client, newObj, prevObj *vmv1be rclient.Scheme().Default(newObj) nsn := types.NamespacedName{Name: newObj.Name, Namespace: newObj.Namespace} removeFinalizer := false + + var generation int64 err := retryOnConflict(func() error { var existingObj vmv1beta1.VMAgent if err := rclient.Get(ctx, nsn, &existingObj); err != nil { @@ -32,6 +34,7 @@ func VMAgent(ctx context.Context, rclient client.Client, newObj, prevObj *vmv1be if err := rclient.Create(ctx, newObj); err != nil { return fmt.Errorf("cannot create new VMAgent=%s: %w", nsn.String(), err) } + generation = newObj.Generation return nil } return fmt.Errorf("cannot get VMAgent=%s: %w", nsn.String(), err) @@ -54,12 +57,13 @@ func VMAgent(ctx context.Context, rclient client.Client, newObj, prevObj *vmv1be if err := rclient.Update(ctx, &existingObj); err != nil { return fmt.Errorf("cannot update VMAgent=%s: %w", nsn.String(), err) } + generation = existingObj.Generation return nil }) if err != nil { return err } - if err := waitForStatus(ctx, rclient, newObj, vmWaitReadyInterval, vmv1beta1.UpdateStatusOperational); err != nil { + if err := waitForStatus(ctx, rclient, newObj, vmWaitReadyInterval, vmv1beta1.UpdateStatusOperational, generation); err != nil { return fmt.Errorf("failed to wait for VMAgent=%s to be ready: %w", nsn.String(), err) } return nil diff --git a/internal/controller/operator/factory/reconcile/vmauth.go b/internal/controller/operator/factory/reconcile/vmauth.go index 5afd2a877..728340b7f 100644 --- a/internal/controller/operator/factory/reconcile/vmauth.go +++ b/internal/controller/operator/factory/reconcile/vmauth.go @@ -23,6 +23,7 @@ func VMAuth(ctx context.Context, rclient client.Client, newObj, prevObj *vmv1bet rclient.Scheme().Default(newObj) nsn := types.NamespacedName{Name: newObj.Name, Namespace: newObj.Namespace} removeFinalizer := false + var generation int64 err := retryOnConflict(func() error { var existingObj vmv1beta1.VMAuth if err := rclient.Get(ctx, nsn, &existingObj); err != nil { @@ -31,6 +32,7 @@ func VMAuth(ctx context.Context, rclient client.Client, newObj, prevObj *vmv1bet if err := rclient.Create(ctx, newObj); err != nil { return fmt.Errorf("cannot create new VMAuth=%s: %w", nsn.String(), err) } + generation = newObj.Generation return nil } return fmt.Errorf("cannot get VMAuth=%s: %w", nsn.String(), err) @@ -53,12 +55,13 @@ func VMAuth(ctx context.Context, rclient client.Client, newObj, prevObj *vmv1bet if err := rclient.Update(ctx, &existingObj); err != nil { return fmt.Errorf("cannot update VMAuth=%s: %w", nsn.String(), err) } + generation = existingObj.Generation return nil }) if err != nil { return err } - if err := waitForStatus(ctx, rclient, newObj, vmWaitReadyInterval, vmv1beta1.UpdateStatusOperational); err != nil { + if err := waitForStatus(ctx, rclient, newObj, vmWaitReadyInterval, vmv1beta1.UpdateStatusOperational, generation); err != nil { return fmt.Errorf("failed to wait for VMAuth=%s to be ready: %w", nsn.String(), err) } return nil diff --git a/internal/controller/operator/factory/reconcile/vmcluster.go b/internal/controller/operator/factory/reconcile/vmcluster.go index b85aa7547..93162e178 100644 --- a/internal/controller/operator/factory/reconcile/vmcluster.go +++ b/internal/controller/operator/factory/reconcile/vmcluster.go @@ -23,6 +23,7 @@ func VMCluster(ctx context.Context, rclient client.Client, newObj, prevObj *vmv1 rclient.Scheme().Default(newObj) nsn := types.NamespacedName{Name: newObj.Name, Namespace: newObj.Namespace} removeFinalizer := false + var generation int64 err := retryOnConflict(func() error { var existingObj vmv1beta1.VMCluster if err := rclient.Get(ctx, nsn, &existingObj); err != nil { @@ -31,6 +32,7 @@ func VMCluster(ctx context.Context, rclient client.Client, newObj, prevObj *vmv1 if err := rclient.Create(ctx, newObj); err != nil { return fmt.Errorf("cannot create new VMCluster=%s: %w", nsn.String(), err) } + generation = newObj.Generation return nil } return fmt.Errorf("cannot get VMCluster=%s: %w", nsn.String(), err) @@ -53,12 +55,13 @@ func VMCluster(ctx context.Context, rclient client.Client, newObj, prevObj *vmv1 if err := rclient.Update(ctx, &existingObj); err != nil { return fmt.Errorf("cannot update VMCluster=%s: %w", nsn.String(), err) } + generation = existingObj.Generation return nil }) if err != nil { return err } - if err := waitForStatus(ctx, rclient, newObj, vmWaitReadyInterval, vmv1beta1.UpdateStatusOperational); err != nil { + if err := waitForStatus(ctx, rclient, newObj, vmWaitReadyInterval, vmv1beta1.UpdateStatusOperational, generation); err != nil { return fmt.Errorf("failed to wait for VMCluster=%s to be ready: %w", nsn.String(), err) } return nil diff --git a/internal/controller/operator/factory/vmdistributed/vmauth.go b/internal/controller/operator/factory/vmdistributed/vmauth.go index fab27ca50..a0561094a 100644 --- a/internal/controller/operator/factory/vmdistributed/vmauth.go +++ b/internal/controller/operator/factory/vmdistributed/vmauth.go @@ -88,8 +88,15 @@ func buildVMAuthLB(cr *vmv1alpha1.VMDistributed, vmAgents []*vmv1beta1.VMAgent, } var targetRefs []vmv1beta1.TargetRef owner := cr.AsOwner() - targetRefs = append(targetRefs, vmAgentTargetRef(vmAgents, &owner, excludeIds...)) - targetRefs = append(targetRefs, vmClusterTargetRef(vmClusters, &owner, excludeIds...)) + if ref := vmAgentTargetRef(vmAgents, &owner, excludeIds...); len(ref.Static.URLs) > 0 { + targetRefs = append(targetRefs, ref) + } + if ref := vmClusterTargetRef(vmClusters, &owner, excludeIds...); len(ref.Static.URLs) > 0 { + targetRefs = append(targetRefs, ref) + } + if len(targetRefs) == 0 { + return nil + } vmAuth.Spec.UnauthorizedUserAccessSpec.URLMap = nil vmAuth.Spec.UnauthorizedUserAccessSpec.URLPrefix = nil vmAuth.Spec.UnauthorizedUserAccessSpec.TargetRefs = targetRefs diff --git a/internal/controller/operator/factory/vmdistributed/vmdistributed_reconcile_test.go b/internal/controller/operator/factory/vmdistributed/vmdistributed_reconcile_test.go index 90f80df12..18141002b 100644 --- a/internal/controller/operator/factory/vmdistributed/vmdistributed_reconcile_test.go +++ b/internal/controller/operator/factory/vmdistributed/vmdistributed_reconcile_test.go @@ -2,11 +2,19 @@ package vmdistributed import ( "context" + "fmt" + "net" + "net/http" + "net/http/httptest" + "net/url" + "strconv" "testing" "testing/synctest" "github.com/stretchr/testify/assert" + discoveryv1 "k8s.io/api/discovery/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" "k8s.io/utils/ptr" @@ -26,20 +34,66 @@ func Test_CreateOrUpdate_Actions(t *testing.T) { err error } - f := func(args args, want want) { - t.Helper() + name := "test-dist" + namespace := "default" + zoneName := "zone-1" + vmClusterName := types.NamespacedName{Namespace: namespace, Name: "test-dist-zone-1"} + vmAgentName := types.NamespacedName{Namespace: namespace, Name: "test-dist-zone-1"} + vmAuthLBName := types.NamespacedName{Namespace: namespace, Name: name} - fclient := k8stools.GetTestClientWithActionsAndObjects(nil) - ctx := context.TODO() - build.AddDefaults(fclient.Scheme()) - fclient.Scheme().Default(args.cr) + objectMeta := metav1.ObjectMeta{Name: name, Namespace: namespace} + f := func(args args, want want) { synctest.Test(t, func(t *testing.T) { + mux := http.NewServeMux() + handler := func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, `%s{path="/tmp/1_19F3EC170A0DA31A"} 0`, vmAgentQueueMetricName) + } + mux.HandleFunc("/metrics", handler) + ts := httptest.NewServer(mux) + defer ts.Close() + tsURL, err := url.Parse(ts.URL) + assert.NoError(t, err) + tsHost, tsPortStr, err := net.SplitHostPort(tsURL.Host) + if err != nil { + assert.NoError(t, err) + } + tsPort, err := strconv.ParseInt(tsPortStr, 10, 32) + if err != nil { + assert.NoError(t, err) + } + endpointSlice := discoveryv1.EndpointSlice{ + ObjectMeta: metav1.ObjectMeta{ + Name: "random-endpoint-name", + Namespace: namespace, + Labels: map[string]string{discoveryv1.LabelServiceName: "vmagent-" + vmAgentName.Name}, + }, + Endpoints: []discoveryv1.Endpoint{ + { + Addresses: []string{tsHost}, + Conditions: discoveryv1.EndpointConditions{ + Ready: ptr.To(true), + }, + }, + }, + Ports: []discoveryv1.EndpointPort{ + { + Name: ptr.To("http"), + Port: ptr.To[int32](int32(tsPort)), + }, + }, + } + fclient := k8stools.GetTestClientWithOptsActionsAndObjects([]runtime.Object{&endpointSlice}, &k8stools.ClientOpts{ + SetCreationTimestamp: true, + }) + ctx := context.TODO() + build.AddDefaults(fclient.Scheme()) + fclient.Scheme().Default(args.cr) if args.preRun != nil { args.preRun(ctx, fclient, args.cr) } - err := CreateOrUpdate(ctx, args.cr, fclient) + err = CreateOrUpdate(ctx, args.cr, fclient) if want.err != nil { assert.Error(t, err) } else { @@ -63,15 +117,6 @@ func Test_CreateOrUpdate_Actions(t *testing.T) { }) } - name := "test-dist" - namespace := "default" - zoneName := "zone-1" - vmClusterName := types.NamespacedName{Namespace: namespace, Name: "test-dist-zone-1"} - vmAgentName := types.NamespacedName{Namespace: namespace, Name: "test-dist-zone-1"} - vmAuthLBName := types.NamespacedName{Namespace: namespace, Name: name} - - objectMeta := metav1.ObjectMeta{Name: name, Namespace: namespace} - // default f(args{ cr: &vmv1alpha1.VMDistributed{ @@ -116,29 +161,28 @@ func Test_CreateOrUpdate_Actions(t *testing.T) { }, }, }, - }, - want{ - actions: []k8stools.ClientAction{ - // getZones - {Verb: "Get", Kind: "VMCluster", Resource: vmClusterName}, - {Verb: "Get", Kind: "VMAgent", Resource: vmAgentName}, + }, want{ + actions: []k8stools.ClientAction{ + // getZones + {Verb: "Get", Kind: "VMCluster", Resource: vmClusterName}, + {Verb: "Get", Kind: "VMAgent", Resource: vmAgentName}, - // reconcile VMCluster - {Verb: "Get", Kind: "VMCluster", Resource: vmClusterName}, - {Verb: "Create", Kind: "VMCluster", Resource: vmClusterName}, - {Verb: "Get", Kind: "VMCluster", Resource: vmClusterName}, + // reconcile VMCluster + {Verb: "Get", Kind: "VMCluster", Resource: vmClusterName}, + {Verb: "Create", Kind: "VMCluster", Resource: vmClusterName}, + {Verb: "Get", Kind: "VMCluster", Resource: vmClusterName}, - // reconcile VMAgent - {Verb: "Get", Kind: "VMAgent", Resource: vmAgentName}, - {Verb: "Create", Kind: "VMAgent", Resource: vmAgentName}, - {Verb: "Get", Kind: "VMAgent", Resource: vmAgentName}, + // reconcile VMAgent + {Verb: "Get", Kind: "VMAgent", Resource: vmAgentName}, + {Verb: "Create", Kind: "VMAgent", Resource: vmAgentName}, + {Verb: "Get", Kind: "VMAgent", Resource: vmAgentName}, - // reconcile VMAuth - {Verb: "Get", Kind: "VMAuth", Resource: vmAuthLBName}, - {Verb: "Create", Kind: "VMAuth", Resource: vmAuthLBName}, - {Verb: "Get", Kind: "VMAuth", Resource: vmAuthLBName}, - }, - }) + // reconcile VMAuth + {Verb: "Get", Kind: "VMAuth", Resource: vmAuthLBName}, + {Verb: "Create", Kind: "VMAuth", Resource: vmAuthLBName}, + {Verb: "Get", Kind: "VMAuth", Resource: vmAuthLBName}, + }, + }) // create vmagent f(args{ @@ -180,24 +224,23 @@ func Test_CreateOrUpdate_Actions(t *testing.T) { }, }, }, - }, - want{ - actions: []k8stools.ClientAction{ - // getZones - {Verb: "Get", Kind: "VMCluster", Resource: vmClusterName}, - {Verb: "Get", Kind: "VMAgent", Resource: vmAgentName}, + }, want{ + actions: []k8stools.ClientAction{ + // getZones + {Verb: "Get", Kind: "VMCluster", Resource: vmClusterName}, + {Verb: "Get", Kind: "VMAgent", Resource: vmAgentName}, - // reconcile VMCluster - {Verb: "Get", Kind: "VMCluster", Resource: vmClusterName}, - {Verb: "Create", Kind: "VMCluster", Resource: vmClusterName}, - {Verb: "Get", Kind: "VMCluster", Resource: vmClusterName}, + // reconcile VMCluster + {Verb: "Get", Kind: "VMCluster", Resource: vmClusterName}, + {Verb: "Create", Kind: "VMCluster", Resource: vmClusterName}, + {Verb: "Get", Kind: "VMCluster", Resource: vmClusterName}, - // reconcile VMAgent - {Verb: "Get", Kind: "VMAgent", Resource: vmAgentName}, - {Verb: "Create", Kind: "VMAgent", Resource: vmAgentName}, - {Verb: "Get", Kind: "VMAgent", Resource: vmAgentName}, - }, - }) + // reconcile VMAgent + {Verb: "Get", Kind: "VMAgent", Resource: vmAgentName}, + {Verb: "Create", Kind: "VMAgent", Resource: vmAgentName}, + {Verb: "Get", Kind: "VMAgent", Resource: vmAgentName}, + }, + }) // no change on status update f(args{ @@ -253,24 +296,23 @@ func Test_CreateOrUpdate_Actions(t *testing.T) { // Update status to simulate consistency cr.Status.UpdateStatus = vmv1beta1.UpdateStatusOperational }, - }, - want{ - actions: []k8stools.ClientAction{ - // getZones - {Verb: "Get", Kind: "VMCluster", Resource: vmClusterName}, - {Verb: "Get", Kind: "VMAgent", Resource: vmAgentName}, + }, want{ + actions: []k8stools.ClientAction{ + // getZones + {Verb: "Get", Kind: "VMCluster", Resource: vmClusterName}, + {Verb: "Get", Kind: "VMAgent", Resource: vmAgentName}, - // reconcile VMCluster - {Verb: "Get", Kind: "VMCluster", Resource: vmClusterName}, - {Verb: "Get", Kind: "VMCluster", Resource: vmClusterName}, + // reconcile VMCluster + {Verb: "Get", Kind: "VMCluster", Resource: vmClusterName}, + {Verb: "Get", Kind: "VMCluster", Resource: vmClusterName}, - // reconcile VMAgent - {Verb: "Get", Kind: "VMAgent", Resource: vmAgentName}, - {Verb: "Get", Kind: "VMAgent", Resource: vmAgentName}, + // reconcile VMAgent + {Verb: "Get", Kind: "VMAgent", Resource: vmAgentName}, + {Verb: "Get", Kind: "VMAgent", Resource: vmAgentName}, - // reconcile VMAuth - {Verb: "Get", Kind: "VMAuth", Resource: vmAuthLBName}, - {Verb: "Get", Kind: "VMAuth", Resource: vmAuthLBName}, - }, - }) + // reconcile VMAuth + {Verb: "Get", Kind: "VMAuth", Resource: vmAuthLBName}, + {Verb: "Get", Kind: "VMAuth", Resource: vmAuthLBName}, + }, + }) }