Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions api/operator/v1beta1/vmcluster_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ type VMClusterSpec struct {
// +optional
License *License `json:"license,omitempty"`

// Downsampling defines downsampling rules applied to vmselect and vmstorage components.
// Requires enterprise license. See https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/#downsampling
// +optional
Downsampling *DownsamplingConfig `json:"downsampling,omitempty"`

// +optional
VMSelect *VMSelect `json:"vmselect,omitempty"`
// +optional
Expand Down Expand Up @@ -462,6 +467,10 @@ type VMStorage struct {
// VMBackup configuration for backup
// +optional
VMBackup *VMBackup `json:"vmBackup,omitempty"`
// RetentionFilters defines per-series retention filters for vmstorage.
// Requires enterprise license. See https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/#retention-filters
// +optional
RetentionFilters []RetentionFilter `json:"retentionFilters,omitempty"`
// ServiceSpec that will be create additional service for vmstorage
// +optional
ServiceSpec *AdditionalServiceSpec `json:"serviceSpec,omitempty"`
Expand Down Expand Up @@ -679,6 +688,9 @@ func (cr *VMCluster) Validate() error {
}
}
}
if err := cr.Spec.Downsampling.validate(cr.Spec.License); err != nil {
return err
}
if cr.Spec.VMStorage != nil {
vms := cr.Spec.VMStorage
name := cr.PrefixedName(ClusterComponentSelect)
Expand All @@ -690,6 +702,22 @@ func (cr *VMCluster) Validate() error {
return err
}
}
if len(vms.RetentionFilters) > 0 {
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
if !cr.Spec.License.IsProvided() {
return fmt.Errorf("it is required to provide license key for retentionFilters. See [here](https://docs.victoriametrics.com/victoriametrics/enterprise/)")
}
if err := cr.Spec.License.validate(); err != nil {
return err
}
for i := range vms.RetentionFilters {
if err := vms.RetentionFilters[i].validate(); err != nil {
return fmt.Errorf("vmstorage.retentionFilters[%d]: %w", i, err)
}
}
if err := validateRetentionFiltersAgainstPeriod(vms.RetentionFilters, cr.Spec.RetentionPeriod, "vmstorage.retentionFilters"); err != nil {
return err
}
}
if vms.RollingUpdateStrategyBehavior != nil {
if err := vms.RollingUpdateStrategyBehavior.Validate(); err != nil {
return fmt.Errorf("vmstorage: %w", err)
Expand Down
142 changes: 142 additions & 0 deletions api/operator/v1beta1/vmcluster_types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,3 +121,145 @@ func TestVMCluster_AvailableStorageNodeIDs(t *testing.T) {
},
}, ClusterComponentSelect, []int32{0, 1, 2})
}

func TestVMCluster_Validate(t *testing.T) {
f := func(spec VMClusterSpec, wantErr bool) {
t.Helper()
cr := &VMCluster{Spec: spec}
if wantErr {
assert.Error(t, cr.Validate())
} else {
assert.NoError(t, cr.Validate())
}
}

// empty spec
f(VMClusterSpec{}, false)

// downsampling without license
f(VMClusterSpec{
Downsampling: &DownsamplingConfig{
Rules: []DownsamplingRule{{Periods: []DownsamplingPeriod{{Offset: "30d", Interval: "10m"}}}},
},
}, true)

// downsampling with valid config
f(VMClusterSpec{
License: testLicense,
Downsampling: &DownsamplingConfig{
Rules: []DownsamplingRule{{Periods: []DownsamplingPeriod{{Offset: "30d", Interval: "10m"}}}},
},
}, false)

// downsampling with filter and dedupInterval
f(VMClusterSpec{
License: testLicense,
Downsampling: &DownsamplingConfig{
Rules: []DownsamplingRule{{Filter: `{env="prod"}`, Periods: []DownsamplingPeriod{{Offset: "90d", Interval: "1h"}}}},
DedupInterval: "1m",
},
}, false)

// downsampling - multiple periods per rule
f(VMClusterSpec{
License: testLicense,
Downsampling: &DownsamplingConfig{
Rules: []DownsamplingRule{{Periods: []DownsamplingPeriod{
{Offset: "30d", Interval: "10m"},
{Offset: "180d", Interval: "1h"},
}}},
},
}, false)

// downsampling - duplicate filter
f(VMClusterSpec{
License: testLicense,
Downsampling: &DownsamplingConfig{
Rules: []DownsamplingRule{
{Periods: []DownsamplingPeriod{{Offset: "30d", Interval: "10m"}}},
{Periods: []DownsamplingPeriod{{Offset: "180d", Interval: "1h"}}},
},
},
}, true)

// downsampling - offset not a multiple of interval
f(VMClusterSpec{
License: testLicense,
Downsampling: &DownsamplingConfig{
Rules: []DownsamplingRule{{Periods: []DownsamplingPeriod{{Offset: "1d", Interval: "7m"}}}},
},
}, true)

// downsampling - period interval not a multiple of dedupInterval
f(VMClusterSpec{
License: testLicense,
Downsampling: &DownsamplingConfig{
Rules: []DownsamplingRule{{Periods: []DownsamplingPeriod{{Offset: "30d", Interval: "10m"}}}},
DedupInterval: "7m",
},
}, true)

// downsampling - period interval is a multiple of dedupInterval
f(VMClusterSpec{
License: testLicense,
Downsampling: &DownsamplingConfig{
Rules: []DownsamplingRule{{Periods: []DownsamplingPeriod{{Offset: "30d", Interval: "10m"}}}},
DedupInterval: "5m",
},
}, false)

// retention filters without vmstorage section — no error (vmstorage is nil)
f(VMClusterSpec{
License: testLicense,
}, false)

// retention filters without license
f(VMClusterSpec{
VMStorage: &VMStorage{
RetentionFilters: []RetentionFilter{{Filter: `{env="dev"}`, Retention: "3d"}},
},
}, true)

// retention filters with valid config
f(VMClusterSpec{
License: testLicense,
RetentionPeriod: "30",
VMStorage: &VMStorage{
RetentionFilters: []RetentionFilter{{Filter: `{env="dev"}`, Retention: "3d"}},
},
}, false)

// retention filters - invalid filter
f(VMClusterSpec{
License: testLicense,
VMStorage: &VMStorage{
RetentionFilters: []RetentionFilter{{Filter: "not-a-filter", Retention: "3d"}},
},
}, true)

// retention filters - invalid retention
f(VMClusterSpec{
License: testLicense,
VMStorage: &VMStorage{
RetentionFilters: []RetentionFilter{{Filter: `{env="dev"}`, Retention: "bad"}},
},
}, true)

// retention filters - retention exceeds retentionPeriod
f(VMClusterSpec{
License: testLicense,
RetentionPeriod: "30d",
VMStorage: &VMStorage{
RetentionFilters: []RetentionFilter{{Filter: `{env="dev"}`, Retention: "1y"}},
},
}, true)

// retention filters - retention equal to retentionPeriod is ok
f(VMClusterSpec{
License: testLicense,
RetentionPeriod: "1y",
VMStorage: &VMStorage{
RetentionFilters: []RetentionFilter{{Filter: `{env="dev"}`, Retention: "1y"}},
},
}, false)
}
Loading