diff --git a/api/operator/v1/vlagent_types.go b/api/operator/v1/vlagent_types.go index 5e6160440..45b757b96 100644 --- a/api/operator/v1/vlagent_types.go +++ b/api/operator/v1/vlagent_types.go @@ -55,10 +55,10 @@ type VLAgentSpec struct { // PodDisruptionBudget created by operator // +optional PodDisruptionBudget *vmv1beta1.EmbeddedPodDisruptionBudgetSpec `json:"podDisruptionBudget,omitempty"` - // StatefulStorage configures storage for StatefulSet + // Storage configures storage for StatefulSet // +optional Storage *vmv1beta1.StorageSpec `json:"storage,omitempty"` - // StatefulRollingUpdateStrategy allows configuration for strategyType + // RollingUpdateStrategy allows configuration for strategyType // set it to RollingUpdate for disabling operator statefulSet rollingUpdate // +optional RollingUpdateStrategy appsv1.StatefulSetUpdateStrategyType `json:"rollingUpdateStrategy,omitempty"` diff --git a/config/crd/overlay/crd.yaml b/config/crd/overlay/crd.yaml index d29373e27..f31cffc83 100644 --- a/config/crd/overlay/crd.yaml +++ b/config/crd/overlay/crd.yaml @@ -1267,7 +1267,7 @@ spec: type: integer rollingUpdateStrategy: description: |- - StatefulRollingUpdateStrategy allows configuration for strategyType + RollingUpdateStrategy allows configuration for strategyType set it to RollingUpdate for disabling operator statefulSet rollingUpdate type: string runtimeClassName: @@ -1357,7 +1357,7 @@ spec: type: object x-kubernetes-preserve-unknown-fields: true storage: - description: StatefulStorage configures storage for StatefulSet + description: Storage configures storage for StatefulSet properties: disableMountSubPath: description: |- diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 0552f5341..9a9d3ab0f 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -29,6 +29,7 @@ aliases: * FEATURE: [vmuser](https://docs.victoriametrics.com/operator/resources/vmuser/): introduce `spec.managedMetadata` for custom labels and annotations that should be attached to a Secret. * FEATURE: [vmuser](https://docs.victoriametrics.com/operator/resources/vmuser/): introduce `query_args` parameter that allows to append query arguments for backend url generation +* BUGFIX: [vmsingle](https://docs.victoriametrics.com/operator/resources/vmsingle/), [vlsingle](https://docs.victoriametrics.com/operator/resources/vlsingle/) and [vmalertmanager](https://docs.victoriametrics.com/operator/resources/vmalertmanager): do not mount emptydir if storage data volume is already present in volumes list. Before it was impossible to mount external PVC without overriding default storageDataPath using `spec.extraArgs` and without having unneeded emptydir listed among pod volumes. Related issues [#1477](https://github.com/VictoriaMetrics/operator/issues/1477). * BUGFIX: [vmalertmanager](https://docs.victoriametrics.com/operator/resources/vmalertmanager/): check `mute_time_intervals` in subroutes: See [#1618](https://github.com/VictoriaMetrics/operator/issues/1618). * BUGFIX: [vmoperator](https://docs.victoriametrics.com/operator/): remove incorrect key argument in structured log for when the actual PVC storage size is larger than the currently configured size and properly indicate which is the new and which is the existing size: See PR [#1636](https://github.com/VictoriaMetrics/operator/pull/1636) for details. * BUGFIX: [converter](https://docs.victoriametrics.com/operator/integrations/prometheus/#objects-conversion): properly convert Prometheus ScrapeConfig `scrapeInterval` into VM ScrapeConfig `scrape_interval`. Before it was ignored. See [#1645](https://github.com/VictoriaMetrics/operator/issues/1645). diff --git a/docs/api.md b/docs/api.md index b2154c9f0..7a1381b5c 100644 --- a/docs/api.md +++ b/docs/api.md @@ -253,7 +253,7 @@ Appears in: [VLAgent](#vlagent) | replicaCount#
_integer_ | _(Optional)_
ReplicaCount is the expected size of the Application. | | resources#
_[ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#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. | -| rollingUpdateStrategy#
_[StatefulSetUpdateStrategyType](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#statefulsetupdatestrategytype-v1-apps)_ | _(Optional)_
StatefulRollingUpdateStrategy allows configuration for strategyType
set it to RollingUpdate for disabling operator statefulSet rollingUpdate | +| rollingUpdateStrategy#
_[StatefulSetUpdateStrategyType](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#statefulsetupdatestrategytype-v1-apps)_ | _(Optional)_
RollingUpdateStrategy allows configuration for strategyType
set it to RollingUpdate for disabling operator statefulSet rollingUpdate | | 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 | @@ -261,7 +261,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 | -| storage#
_[StorageSpec](#storagespec)_ | _(Optional)_
StatefulStorage configures storage for StatefulSet | +| 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 | | tolerations#
_[Toleration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#toleration-v1-core) array_ | _(Optional)_
Tolerations If specified, the pod's tolerations. | diff --git a/internal/controller/operator/factory/build/container.go b/internal/controller/operator/factory/build/container.go index edc7e4da5..93cdc326b 100644 --- a/internal/controller/operator/factory/build/container.go +++ b/internal/controller/operator/factory/build/container.go @@ -535,3 +535,47 @@ func AddSyslogTLSConfigToVolumes(dstVolumes []corev1.Volume, dstMounts []corev1. } return dstVolumes, dstMounts } + +func StorageVolumeMountsTo(volumes []corev1.Volume, mounts []corev1.VolumeMount, pvcSrc *corev1.PersistentVolumeClaimVolumeSource, volumeName, storagePath string) ([]corev1.Volume, []corev1.VolumeMount) { + var alreadyMounted bool + for _, volumeMount := range mounts { + if volumeMount.Name == volumeName { + alreadyMounted = true + break + } + } + if !alreadyMounted { + mounts = append(mounts, corev1.VolumeMount{ + Name: volumeName, + MountPath: storagePath, + }) + } + if pvcSrc != nil { + volumes = append(volumes, corev1.Volume{ + Name: volumeName, + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: pvcSrc, + }, + }) + return volumes, mounts + } + + var volumePresent bool + for _, volume := range volumes { + if volume.Name == volumeName { + volumePresent = true + break + } + } + if volumePresent { + return volumes, mounts + } + + volumes = append(volumes, corev1.Volume{ + Name: volumeName, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }, + }) + return volumes, mounts +} diff --git a/internal/controller/operator/factory/build/container_test.go b/internal/controller/operator/factory/build/container_test.go index 9f26d8c12..32e5ce653 100644 --- a/internal/controller/operator/factory/build/container_test.go +++ b/internal/controller/operator/factory/build/container_test.go @@ -45,7 +45,6 @@ func Test_buildProbe(t *testing.T) { cr testBuildProbeCR validate func(corev1.Container) error } - f := func(o opts) { t.Helper() got := Probe(o.container, o.cr) @@ -349,5 +348,180 @@ func TestAddSyslogArgsTo(t *testing.T) { "-syslog.compressMethod.udp=zstd", } f(&spec, expected) +} + +func TestStorageVolumeMountsTo(t *testing.T) { + type opts struct { + pvcSrc *corev1.PersistentVolumeClaimVolumeSource + volumeName string + storagePath string + volumes []corev1.Volume + expectedVolumes []corev1.Volume + mounts []corev1.VolumeMount + expectedMounts []corev1.VolumeMount + } + f := func(o opts) { + t.Helper() + gotVolumes, gotMounts := StorageVolumeMountsTo(o.volumes, o.mounts, o.pvcSrc, o.volumeName, o.storagePath) + assert.Equal(t, o.expectedMounts, gotMounts) + assert.Equal(t, o.expectedVolumes, gotVolumes) + } + + // no PVC spec and no volumes and mounts + f(opts{ + volumeName: "test", + storagePath: "/test", + expectedVolumes: []corev1.Volume{{ + Name: "test", + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }, + }}, + expectedMounts: []corev1.VolumeMount{{ + Name: "test", + MountPath: "/test", + }}, + }) + + // with PVC spec and no volumes and mounts + f(opts{ + volumeName: "test", + storagePath: "/test", + pvcSrc: &corev1.PersistentVolumeClaimVolumeSource{ + ClaimName: "test-claim", + }, + expectedVolumes: []corev1.Volume{{ + Name: "test", + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ + ClaimName: "test-claim", + }, + }, + }}, + expectedMounts: []corev1.VolumeMount{{ + Name: "test", + MountPath: "/test", + }}, + }) + + // with PVC spec and matching data volume + f(opts{ + volumes: []corev1.Volume{{ + Name: "test", + VolumeSource: corev1.VolumeSource{ + AWSElasticBlockStore: &corev1.AWSElasticBlockStoreVolumeSource{ + VolumeID: "aws-volume", + }, + }, + }}, + volumeName: "test", + storagePath: "/test", + pvcSrc: &corev1.PersistentVolumeClaimVolumeSource{ + ClaimName: "test-claim", + }, + expectedVolumes: []corev1.Volume{ + { + Name: "test", + VolumeSource: corev1.VolumeSource{ + AWSElasticBlockStore: &corev1.AWSElasticBlockStoreVolumeSource{ + VolumeID: "aws-volume", + }, + }, + }, + { + Name: "test", + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ + ClaimName: "test-claim", + }, + }, + }, + }, + expectedMounts: []corev1.VolumeMount{{ + Name: "test", + MountPath: "/test", + }}, + }) + // with PVC spec and not matching data volume + f(opts{ + volumes: []corev1.Volume{{ + Name: "extra", + VolumeSource: corev1.VolumeSource{ + AWSElasticBlockStore: &corev1.AWSElasticBlockStoreVolumeSource{ + VolumeID: "aws-volume", + }, + }, + }}, + volumeName: "test", + storagePath: "/test", + pvcSrc: &corev1.PersistentVolumeClaimVolumeSource{ + ClaimName: "test-claim", + }, + expectedVolumes: []corev1.Volume{ + { + Name: "extra", + VolumeSource: corev1.VolumeSource{ + AWSElasticBlockStore: &corev1.AWSElasticBlockStoreVolumeSource{ + VolumeID: "aws-volume", + }, + }, + }, + { + Name: "test", + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ + ClaimName: "test-claim", + }, + }, + }, + }, + expectedMounts: []corev1.VolumeMount{{ + Name: "test", + MountPath: "/test", + }}, + }) + + // with PVC spec and existing data volume mount + f(opts{ + volumes: []corev1.Volume{{ + Name: "extra", + VolumeSource: corev1.VolumeSource{ + AWSElasticBlockStore: &corev1.AWSElasticBlockStoreVolumeSource{ + VolumeID: "aws-volume", + }, + }, + }}, + mounts: []corev1.VolumeMount{{ + Name: "test", + MountPath: "/other-path", + }}, + volumeName: "test", + storagePath: "/test", + pvcSrc: &corev1.PersistentVolumeClaimVolumeSource{ + ClaimName: "test-claim", + }, + expectedVolumes: []corev1.Volume{ + { + Name: "extra", + VolumeSource: corev1.VolumeSource{ + AWSElasticBlockStore: &corev1.AWSElasticBlockStoreVolumeSource{ + VolumeID: "aws-volume", + }, + }, + }, + { + Name: "test", + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ + ClaimName: "test-claim", + }, + }, + }, + }, + expectedMounts: []corev1.VolumeMount{{ + Name: "test", + MountPath: "/other-path", + }}, + }) } diff --git a/internal/controller/operator/factory/vlsingle/vlogs.go b/internal/controller/operator/factory/vlsingle/vlogs.go index da5c887b5..61db98bac 100644 --- a/internal/controller/operator/factory/vlsingle/vlogs.go +++ b/internal/controller/operator/factory/vlsingle/vlogs.go @@ -150,7 +150,7 @@ func makeVLogsPodSpec(r *vmv1beta1.VLogs) (*corev1.PodTemplateSpec, error) { // if customStorageDataPath is not empty, do not add pvc. shouldAddPVC := r.Spec.StorageDataPath == "" - storagePath := vlsingleDataDir + storagePath := dataDataDir if r.Spec.StorageDataPath != "" { storagePath = r.Spec.StorageDataPath } @@ -186,14 +186,14 @@ func makeVLogsPodSpec(r *vmv1beta1.VLogs) (*corev1.PodTemplateSpec, error) { if storageSpec == nil { volumes = append(volumes, corev1.Volume{ - Name: vlsingleDataVolumeName, + Name: dataVolumeName, VolumeSource: corev1.VolumeSource{ EmptyDir: &corev1.EmptyDirVolumeSource{}, }, }) } else if shouldAddPVC { volumes = append(volumes, corev1.Volume{ - Name: vlsingleDataVolumeName, + Name: dataVolumeName, VolumeSource: corev1.VolumeSource{ PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ ClaimName: r.PrefixedName(), @@ -204,7 +204,7 @@ func makeVLogsPodSpec(r *vmv1beta1.VLogs) (*corev1.PodTemplateSpec, error) { volumes = append(volumes, r.Spec.Volumes...) vmMounts := []corev1.VolumeMount{ { - Name: vlsingleDataVolumeName, + Name: dataVolumeName, MountPath: storagePath, }, } diff --git a/internal/controller/operator/factory/vlsingle/vlsingle.go b/internal/controller/operator/factory/vlsingle/vlsingle.go index 13d1c0a0e..3235e1296 100644 --- a/internal/controller/operator/factory/vlsingle/vlsingle.go +++ b/internal/controller/operator/factory/vlsingle/vlsingle.go @@ -24,8 +24,8 @@ import ( ) const ( - vlsingleDataDir = "/victoria-logs-data" - vlsingleDataVolumeName = "data" + dataDataDir = "/victoria-logs-data" + dataVolumeName = "data" tlsServerConfigMountPath = "/etc/vm/tls-server-secrets" ) @@ -69,7 +69,7 @@ func CreateOrUpdate(ctx context.Context, rclient client.Client, cr *vmv1.VLSingl return err } } - if cr.Spec.Storage != nil && cr.Spec.StorageDataPath == "" { + if cr.Spec.Storage != nil { if err := createOrUpdatePVC(ctx, rclient, cr, prevCR); err != nil { return err } @@ -156,10 +156,7 @@ func makePodSpec(r *vmv1.VLSingle) (*corev1.PodTemplateSpec, error) { args = append(args, fmt.Sprintf("-retention.maxDiskSpaceUsageBytes=%s", r.Spec.RetentionMaxDiskSpaceUsageBytes)) } - // if customStorageDataPath is not empty, do not add pvc. - shouldAddPVC := r.Spec.StorageDataPath == "" - - storagePath := vlsingleDataDir + storagePath := dataDataDir if r.Spec.StorageDataPath != "" { storagePath = r.Spec.StorageDataPath } @@ -193,37 +190,19 @@ func makePodSpec(r *vmv1.VLSingle) (*corev1.PodTemplateSpec, error) { var ports []corev1.ContainerPort ports = append(ports, corev1.ContainerPort{Name: "http", Protocol: "TCP", ContainerPort: intstr.Parse(r.Spec.Port).IntVal}) - volumes := []corev1.Volume{} - - storageSpec := r.Spec.Storage - - if storageSpec == nil { - volumes = append(volumes, corev1.Volume{ - Name: vlsingleDataVolumeName, - VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{}, - }, - }) - } else if shouldAddPVC { - volumes = append(volumes, corev1.Volume{ - Name: vlsingleDataVolumeName, - VolumeSource: corev1.VolumeSource{ - PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ - ClaimName: r.PrefixedName(), - }, - }, - }) - } + var volumes []corev1.Volume + var vmMounts []corev1.VolumeMount volumes = append(volumes, r.Spec.Volumes...) - vmMounts := []corev1.VolumeMount{ - { - Name: vlsingleDataVolumeName, - MountPath: storagePath, - }, - } - vmMounts = append(vmMounts, r.Spec.VolumeMounts...) + var pvcSrc *corev1.PersistentVolumeClaimVolumeSource + if r.Spec.Storage != nil { + pvcSrc = &corev1.PersistentVolumeClaimVolumeSource{ + ClaimName: r.PrefixedName(), + } + } + volumes, vmMounts = build.StorageVolumeMountsTo(volumes, vmMounts, pvcSrc, dataVolumeName, storagePath) + for _, s := range r.Spec.Secrets { volumes = append(volumes, corev1.Volume{ Name: k8stools.SanitizeVolumeName("secret-" + s), diff --git a/internal/controller/operator/factory/vmsingle/vmsingle.go b/internal/controller/operator/factory/vmsingle/vmsingle.go index 79d5e473c..a5330237c 100644 --- a/internal/controller/operator/factory/vmsingle/vmsingle.go +++ b/internal/controller/operator/factory/vmsingle/vmsingle.go @@ -24,8 +24,8 @@ import ( ) const ( - vmSingleDataDir = "/victoria-metrics-data" - vmDataVolumeName = "data" + dataDataDir = "/victoria-metrics-data" + dataVolumeName = "data" streamAggrSecretKey = "config.yaml" ) @@ -83,7 +83,7 @@ func CreateOrUpdate(ctx context.Context, cr *vmv1beta1.VMSingle, rclient client. } } - if cr.Spec.Storage != nil && cr.Spec.StorageDataPath == "" { + if cr.Spec.Storage != nil { if err := createStorage(ctx, rclient, cr, prevCR); err != nil { return fmt.Errorf("cannot create storage: %w", err) } @@ -154,12 +154,7 @@ func makeSpec(ctx context.Context, cr *vmv1beta1.VMSingle) (*corev1.PodTemplateS args = append(args, fmt.Sprintf("-retentionPeriod=%s", cr.Spec.RetentionPeriod)) } - // if customStorageDataPath is not empty, do not add volumes - // and volumeMounts - // it's user responsibility to provide correct values - mustAddVolumeMounts := cr.Spec.StorageDataPath == "" - - storagePath := vmSingleDataDir + storagePath := dataDataDir if cr.Spec.StorageDataPath != "" { storagePath = cr.Spec.StorageDataPath } @@ -191,8 +186,17 @@ func makeSpec(ctx context.Context, cr *vmv1beta1.VMSingle) (*corev1.PodTemplateS var volumes []corev1.Volume var vmMounts []corev1.VolumeMount - volumes, vmMounts = addVolumeMountsTo(volumes, vmMounts, cr, mustAddVolumeMounts, storagePath) + volumes = append(volumes, cr.Spec.Volumes...) + vmMounts = append(vmMounts, cr.Spec.VolumeMounts...) + var pvcSpec *corev1.PersistentVolumeClaimVolumeSource + if cr.Spec.Storage != nil { + pvcSpec = &corev1.PersistentVolumeClaimVolumeSource{ + ClaimName: cr.PrefixedName(), + } + } + + volumes, vmMounts = build.StorageVolumeMountsTo(volumes, vmMounts, pvcSpec, dataVolumeName, storagePath) if cr.Spec.VMBackup != nil && cr.Spec.VMBackup.CredentialsSecret != nil { volumes = append(volumes, corev1.Volume{ Name: k8stools.SanitizeVolumeName("secret-" + cr.Spec.VMBackup.CredentialsSecret.Name), @@ -204,9 +208,6 @@ func makeSpec(ctx context.Context, cr *vmv1beta1.VMSingle) (*corev1.PodTemplateS }) } - volumes = append(volumes, cr.Spec.Volumes...) - vmMounts = append(vmMounts, cr.Spec.VolumeMounts...) - for _, s := range cr.Spec.Secrets { volumes = append(volumes, corev1.Volume{ Name: k8stools.SanitizeVolumeName("secret-" + s), @@ -275,7 +276,7 @@ func makeSpec(ctx context.Context, cr *vmv1beta1.VMSingle) (*corev1.PodTemplateS var initContainers []corev1.Container if cr.Spec.VMBackup != nil { - vmBackupManagerContainer, err := build.VMBackupManager(ctx, cr.Spec.VMBackup, cr.Spec.Port, storagePath, vmDataVolumeName, cr.Spec.ExtraArgs, false, cr.Spec.License) + vmBackupManagerContainer, err := build.VMBackupManager(ctx, cr.Spec.VMBackup, cr.Spec.Port, storagePath, dataVolumeName, cr.Spec.ExtraArgs, false, cr.Spec.License) if err != nil { return nil, err } @@ -285,7 +286,7 @@ func makeSpec(ctx context.Context, cr *vmv1beta1.VMSingle) (*corev1.PodTemplateS if cr.Spec.VMBackup.Restore != nil && cr.Spec.VMBackup.Restore.OnStart != nil && cr.Spec.VMBackup.Restore.OnStart.Enabled { - vmRestore, err := build.VMRestore(cr.Spec.VMBackup, storagePath, vmDataVolumeName) + vmRestore, err := build.VMRestore(cr.Spec.VMBackup, storagePath, dataVolumeName) if err != nil { return nil, err } @@ -451,58 +452,3 @@ func deleteOrphaned(ctx context.Context, rclient client.Client, cr *vmv1beta1.VM return nil } - -func addVolumeMountsTo(volumes []corev1.Volume, vmMounts []corev1.VolumeMount, cr *vmv1beta1.VMSingle, mustAddVolumeMounts bool, storagePath string) ([]corev1.Volume, []corev1.VolumeMount) { - - switch { - case mustAddVolumeMounts: - // add volume and mount point by operator directly - vmMounts = append(vmMounts, corev1.VolumeMount{ - Name: vmDataVolumeName, - MountPath: storagePath}, - ) - - vlSource := corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{}, - } - if cr.Spec.Storage != nil { - vlSource = corev1.VolumeSource{ - PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ - ClaimName: cr.PrefixedName(), - }, - } - } - volumes = append(volumes, corev1.Volume{ - Name: vmDataVolumeName, - VolumeSource: vlSource}) - - case len(cr.Spec.Volumes) > 0: - // add missing volumeMount point for backward compatibility - // it simplifies management of external PVCs - var volumeNamePresent bool - for _, volume := range cr.Spec.Volumes { - if volume.Name == vmDataVolumeName { - volumeNamePresent = true - break - } - } - if volumeNamePresent { - var mustSkipVolumeAdd bool - for _, volumeMount := range cr.Spec.VolumeMounts { - if volumeMount.Name == vmDataVolumeName { - mustSkipVolumeAdd = true - break - } - } - if !mustSkipVolumeAdd { - vmMounts = append(vmMounts, corev1.VolumeMount{ - Name: vmDataVolumeName, - MountPath: storagePath, - }) - } - } - - } - - return volumes, vmMounts -} diff --git a/internal/controller/operator/factory/vtsingle/vtsingle.go b/internal/controller/operator/factory/vtsingle/vtsingle.go index 58ded5470..d55fc11cf 100644 --- a/internal/controller/operator/factory/vtsingle/vtsingle.go +++ b/internal/controller/operator/factory/vtsingle/vtsingle.go @@ -24,8 +24,8 @@ import ( ) const ( - vtsingleDataDir = "/victoria-traces-data" - vtsingleDataVolumeName = "data" + dataDataDir = "/victoria-traces-data" + dataVolumeName = "data" tlsServerConfigMountPath = "/etc/vm/tls-server-secrets" ) @@ -69,7 +69,7 @@ func CreateOrUpdate(ctx context.Context, rclient client.Client, cr *vmv1.VTSingl return err } } - if cr.Spec.Storage != nil && cr.Spec.StorageDataPath == "" { + if cr.Spec.Storage != nil { if err := createOrUpdatePVC(ctx, rclient, cr, prevCR); err != nil { return err } @@ -156,10 +156,7 @@ func makePodSpec(r *vmv1.VTSingle) (*corev1.PodTemplateSpec, error) { args = append(args, fmt.Sprintf("-retention.maxDiskSpaceUsageBytes=%s", r.Spec.RetentionMaxDiskSpaceUsageBytes)) } - // if customStorageDataPath is not empty, do not add pvc. - shouldAddPVC := r.Spec.StorageDataPath == "" - - storagePath := vtsingleDataDir + storagePath := dataDataDir if r.Spec.StorageDataPath != "" { storagePath = r.Spec.StorageDataPath } @@ -193,36 +190,17 @@ func makePodSpec(r *vmv1.VTSingle) (*corev1.PodTemplateSpec, error) { var ports []corev1.ContainerPort ports = append(ports, corev1.ContainerPort{Name: "http", Protocol: "TCP", ContainerPort: intstr.Parse(r.Spec.Port).IntVal}) - volumes := []corev1.Volume{} - - storageSpec := r.Spec.Storage - - if storageSpec == nil { - volumes = append(volumes, corev1.Volume{ - Name: vtsingleDataVolumeName, - VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{}, - }, - }) - } else if shouldAddPVC { - volumes = append(volumes, corev1.Volume{ - Name: vtsingleDataVolumeName, - VolumeSource: corev1.VolumeSource{ - PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ - ClaimName: r.PrefixedName(), - }, - }, - }) - } + var volumes []corev1.Volume + var vmMounts []corev1.VolumeMount volumes = append(volumes, r.Spec.Volumes...) - vmMounts := []corev1.VolumeMount{ - { - Name: vtsingleDataVolumeName, - MountPath: storagePath, - }, - } - vmMounts = append(vmMounts, r.Spec.VolumeMounts...) + var pvcSrc *corev1.PersistentVolumeClaimVolumeSource + if r.Spec.Storage != nil { + pvcSrc = &corev1.PersistentVolumeClaimVolumeSource{ + ClaimName: r.PrefixedName(), + } + } + volumes, vmMounts = build.StorageVolumeMountsTo(volumes, vmMounts, pvcSrc, dataVolumeName, storagePath) for _, s := range r.Spec.Secrets { volumes = append(volumes, corev1.Volume{ diff --git a/test/e2e/vlsingle_test.go b/test/e2e/vlsingle_test.go index bc8cf17bf..fbc84c971 100644 --- a/test/e2e/vlsingle_test.go +++ b/test/e2e/vlsingle_test.go @@ -145,7 +145,6 @@ var _ = Describe("test vlsingle Controller", Label("vl", "single", "vlsingle"), }, RetentionPeriod: "1", StorageDataPath: "/custom-path/internal/dir", - Storage: &corev1.PersistentVolumeClaimSpec{}, }, }, func(cr *vmv1.VLSingle) { @@ -156,8 +155,8 @@ var _ = Describe("test vlsingle Controller", Label("vl", "single", "vlsingle"), Expect(ts.Containers).To(HaveLen(1)) Expect(ts.Volumes).To(HaveLen(2)) Expect(ts.Containers[0].VolumeMounts).To(HaveLen(2)) - Expect(ts.Containers[0].VolumeMounts[0].Name).To(Equal("data")) - Expect(ts.Containers[0].VolumeMounts[1].Name).To(Equal("unused")) + Expect(ts.Containers[0].VolumeMounts[0].Name).To(Equal("unused")) + Expect(ts.Containers[0].VolumeMounts[1].Name).To(Equal("data")) }), ) diff --git a/test/e2e/vmsingle_test.go b/test/e2e/vmsingle_test.go index a50828cf2..9a102f52f 100644 --- a/test/e2e/vmsingle_test.go +++ b/test/e2e/vmsingle_test.go @@ -215,7 +215,7 @@ var _ = Describe("test vmsingle Controller", Label("vm", "single"), func() { Expect(*createdDeploy.Spec.Template.Spec.Containers[0].SecurityContext.RunAsNonRoot).To(BeTrue()) }), - Entry("with data emptyDir", "emptydir", false, + Entry("with storage", "storage", false, &vmv1beta1.VMSingle{ ObjectMeta: metav1.ObjectMeta{ Namespace: namespace, @@ -245,8 +245,34 @@ var _ = Describe("test vmsingle Controller", Label("vm", "single"), func() { Expect(k8sClient.Get(ctx, createdChildObjects, &createdDeploy)).To(Succeed()) ts := createdDeploy.Spec.Template.Spec Expect(ts.Containers).To(HaveLen(1)) - Expect(ts.Volumes).To(BeEmpty()) - Expect(ts.Containers[0].VolumeMounts).To(BeEmpty()) + Expect(ts.Volumes).To(HaveLen(1)) + Expect(ts.Containers[0].VolumeMounts).To(HaveLen(1)) + }), + Entry("with empty dir", "emptydir", false, + &vmv1beta1.VMSingle{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + }, + Spec: vmv1beta1.VMSingleSpec{ + CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ + ReplicaCount: ptr.To[int32](1), + }, + CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ + UseStrictSecurity: ptr.To(false), + }, + RetentionPeriod: "1", + RemovePvcAfterDelete: true, + StorageDataPath: "/tmp/", + }, + }, + func(cr *vmv1beta1.VMSingle) { + createdChildObjects := types.NamespacedName{Namespace: namespace, Name: cr.PrefixedName()} + var createdDeploy appsv1.Deployment + Expect(k8sClient.Get(ctx, createdChildObjects, &createdDeploy)).To(Succeed()) + ts := createdDeploy.Spec.Template.Spec + Expect(ts.Containers).To(HaveLen(1)) + Expect(ts.Volumes).To(HaveLen(1)) + Expect(ts.Containers[0].VolumeMounts).To(HaveLen(1)) }), Entry("with external volume", "externalvolume", true, &vmv1beta1.VMSingle{ @@ -289,7 +315,6 @@ var _ = Describe("test vmsingle Controller", Label("vm", "single"), func() { RetentionPeriod: "1", RemovePvcAfterDelete: true, StorageDataPath: "/custom-path/internal/dir", - Storage: &corev1.PersistentVolumeClaimSpec{}, VMBackup: &vmv1beta1.VMBackup{ Destination: "fs:///opt/backup", VolumeMounts: []corev1.VolumeMount{{Name: "backup", MountPath: "/opt/backup"}}, @@ -304,7 +329,8 @@ var _ = Describe("test vmsingle Controller", Label("vm", "single"), func() { Expect(ts.Containers).To(HaveLen(2)) Expect(ts.Volumes).To(HaveLen(4)) Expect(ts.Containers[0].VolumeMounts).To(HaveLen(3)) - Expect(ts.Containers[0].VolumeMounts[0].Name).To(Equal("data")) + Expect(ts.Containers[0].VolumeMounts[0].Name).To(Equal("unused")) + Expect(ts.Containers[0].VolumeMounts[1].Name).To(Equal("data")) Expect(ts.Containers[1].VolumeMounts).To(HaveLen(3)) Expect(ts.Containers[1].VolumeMounts[0].Name).To(Equal("data")) Expect(ts.Containers[1].VolumeMounts[1].Name).To(Equal("backup")) diff --git a/test/e2e/vtsingle_test.go b/test/e2e/vtsingle_test.go index 1c4ef97eb..239c326c1 100644 --- a/test/e2e/vtsingle_test.go +++ b/test/e2e/vtsingle_test.go @@ -145,7 +145,6 @@ var _ = Describe("test vtsingle Controller", Label("vt", "single", "vtsingle"), }, RetentionPeriod: "1", StorageDataPath: "/custom-path/internal/dir", - Storage: &corev1.PersistentVolumeClaimSpec{}, }, }, func(cr *vmv1.VTSingle) { @@ -156,8 +155,8 @@ var _ = Describe("test vtsingle Controller", Label("vt", "single", "vtsingle"), Expect(ts.Containers).To(HaveLen(1)) Expect(ts.Volumes).To(HaveLen(2)) Expect(ts.Containers[0].VolumeMounts).To(HaveLen(2)) - Expect(ts.Containers[0].VolumeMounts[0].Name).To(Equal("data")) - Expect(ts.Containers[0].VolumeMounts[1].Name).To(Equal("unused")) + Expect(ts.Containers[0].VolumeMounts[0].Name).To(Equal("unused")) + Expect(ts.Containers[0].VolumeMounts[1].Name).To(Equal("data")) }), )