Skip to content
Open
Show file tree
Hide file tree
Changes from 9 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
24 changes: 12 additions & 12 deletions api/operator/v1beta1/vmauth_types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ func TestVMAuthValidate(t *testing.T) {
// invalid ingress
f(opts{
src: `
apiVersion: v1
apiVersion: v1
kind: VMAuth
metadata:
name: must-fail
spec:
ingress:
tlsHosts:
tlsHosts:
- host-1
- host-2`,
wantErr: `spec.ingress.tlsSecretName cannot be empty with non-empty spec.ingress.tlsHosts`,
Expand All @@ -41,7 +41,7 @@ spec:
// both configSecret and external config is defined at the same time
f(opts{
src: `
apiVersion: v1
apiVersion: v1
kind: VMAuth
metadata:
name: must-fail
Expand All @@ -57,21 +57,21 @@ spec:
// incorrect unauthorized access config, missing backends"
f(opts{
src: `
apiVersion: v1
apiVersion: v1
kind: VMAuth
metadata:
name: must-fail
spec:
unauthorizedUserAccessSpec:
default_url:
default_url:
- http://url-1`,
wantErr: "incorrect cr.spec.UnauthorizedUserAccess syntax: at least one of `url_map`, `url_prefix` or `targetRefs` must be defined",
})

// incorrect unauthorized access config, bad metric_labels syntax
f(opts{
src: `
apiVersion: v1
apiVersion: v1
kind: VMAuth
metadata:
name: must-fail
Expand All @@ -80,15 +80,15 @@ spec:
metric_labels:
124124asff: 12fsaf
url_prefix: http://some-dst
default_url:
default_url:
- http://url-1`,
wantErr: `incorrect cr.spec.UnauthorizedUserAccess syntax: incorrect metricLabelName="124124asff", must match pattern="^[a-zA-Z_:.][a-zA-Z0-9_:.]*$"`,
})

// incorrect unauthorized access config url_map"
f(opts{
src: `
apiVersion: v1
apiVersion: v1
kind: VMAuth
metadata:
name: must-fail
Expand All @@ -100,20 +100,20 @@ spec:
- url_prefix: http://some-url
src_paths: ["/path-1"]
- url_prefix: http://some-url-2
default_url:
default_url:
- http://url-1`,
wantErr: `incorrect cr.spec.UnauthorizedUserAccess syntax: incorrect url_map at idx=1: incorrect url_map config at least of one src_paths,src_hosts,src_query_args or src_headers must be defined`,
})

// both unauthorizedUserAccessSpec and UnauthorizedUserAccess defined
f(opts{
src: `
apiVersion: v1
apiVersion: v1
kind: VMAuth
metadata:
name: must-fail
spec:
unauthorizedAccessConfig:
unauthorizedAccessConfig:
- url_prefix: http://some-url
src_paths: ["/path-1"]
- url_prefix: http://some-url-2
Expand All @@ -124,7 +124,7 @@ spec:
url_map:
- url_prefix: http://some-url
src_paths: ["/path-1"]
default_url:
default_url:
- http://url-1`,
wantErr: "at most one option can be used `spec.unauthorizedAccessConfig` or `spec.unauthorizedUserAccessSpec`, got both",
})
Expand Down
2 changes: 2 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ aliases:

* FEATURE: [vmauth](https://docs.victoriametrics.com/operator/resources/vmauth): previously VMAuth could read configuration only from predefined locations; now VMAuth supports arbitrary filesystem access configuration, allowing users to reference required files directly and reducing configuration workarounds. See [#899](https://github.com/VictoriaMetrics/operator/issues/899).

* FEATURE: [vmoperator](https://docs.victoriametrics.com/operator/): added `VM_COMMON_LABELS` and `VM_COMMON_ANNOTATIONS` environment variables to apply common labels/annotations to all Kubernetes resources managed by the operator. These cannot override labels/annotations already set by the operator or via `spec.managedMetadata`. This also ensures HTTPRoutes and PVCs include ManagedMetadata labels and annotations

* BUGFIX: [converter](https://docs.victoriametrics.com/operator/integrations/prometheus/#objects-conversion): disable all prometheus controllers if CRD group was not found. See [#2838](https://github.com/VictoriaMetrics/helm-charts/issues/2838).

## [v0.69.0](https://github.com/VictoriaMetrics/operator/releases/tag/v0.69.0)
Expand Down
2 changes: 2 additions & 0 deletions docs/env.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -623,6 +623,15 @@ type BaseOperatorConf struct {
// - all
// turn off `EnableStrictSecurity` by default, see https://github.com/VictoriaMetrics/operator/issues/749 for details
EnableStrictSecurity bool `default:"false" env:"VM_ENABLESTRICTSECURITY"`

// CommonLabels are added to every Kubernetes resource created by the operator.
// They cannot override labels already set by the operator or via spec.managedMetadata.
// Format: key=value,key2=value2
CommonLabels map[string]string `default:"" env:"VM_COMMON_LABELS" envSeparator:"," envKeyValSeparator:"="`
// CommonAnnotations are added to every Kubernetes resource created by the operator.
// They cannot override annotations already set by the operator or via spec.managedMetadata.
// Format: key=value,key2=value2
CommonAnnotations map[string]string `default:"" env:"VM_COMMON_ANNOTATIONS" envSeparator:"," envKeyValSeparator:"="`
}

// ResyncAfterDuration returns requeue duration for object period reconcile
Expand Down
49 changes: 49 additions & 0 deletions internal/controller/operator/factory/build/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ 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/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/utils/ptr"
Expand Down Expand Up @@ -48,11 +49,19 @@ func AddDefaults(scheme *runtime.Scheme) {
scheme.AddTypeDefaultingFunc(&vmv1.VMAnomaly{}, addVMAnomalyDefaults)
scheme.AddTypeDefaultingFunc(&vmv1beta1.VMServiceScrape{}, addVMServiceScrapeDefaults)
scheme.AddTypeDefaultingFunc(&vmv1alpha1.VMDistributed{}, addVMDistributedDefaults)

scheme.AddTypeDefaultingFunc(&appsv1.DaemonSet{}, addDefaultMetadata)
scheme.AddTypeDefaultingFunc(&corev1.ConfigMap{}, addDefaultMetadata)
scheme.AddTypeDefaultingFunc(&corev1.Namespace{}, addDefaultMetadata)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure this is needed, we don't manage namespaces

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also don't understand why this function should be applied to all core k8s resources? isn't it enough to extend addDefaultMetadata to patch ManagedMetadata? also not sure if this should be applied to pods as well

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather add those preemptively, as some feature may start managing those resources later.

also don't understand why this function should be applied to all core k8s resources?

My understanding is that if the object (VMAgent) creates a deployment and the service, we should make sure both have propagated labels and annotations. I'm not sure that omitting pods / child resources is sufficient for OPA.

also not sure if this should be applied to pods as well

I think so, yeah

scheme.AddTypeDefaultingFunc(&corev1.PersistentVolumeClaim{}, addDefaultMetadata)
scheme.AddTypeDefaultingFunc(&corev1.Secret{}, addDefaultMetadata)
}

func addVMDistributedDefaults(objI any) {
cr := objI.(*vmv1alpha1.VMDistributed)

addDefaultMetadata(cr)

if cr.Spec.ZoneCommon.ReadyTimeout == nil {
cr.Spec.ZoneCommon.ReadyTimeout = &metav1.Duration{
Duration: 5 * time.Minute,
Expand Down Expand Up @@ -81,6 +90,8 @@ func addVMDistributedDefaults(objI any) {
func addStatefulsetDefaults(objI any) {
obj := objI.(*appsv1.StatefulSet)

addDefaultMetadata(obj)

// special case for vm operator defaults
if obj.Spec.UpdateStrategy.Type == "" {
obj.Spec.UpdateStrategy.Type = appsv1.OnDeleteStatefulSetStrategyType
Expand Down Expand Up @@ -118,6 +129,9 @@ func addStatefulsetDefaults(objI any) {
// https://github.com/kubernetes/kubernetes/blob/master/pkg/apis/apps/v1/defaults.go
func addDeploymentDefaults(objI any) {
obj := objI.(*appsv1.Deployment)

addDefaultMetadata(obj)

// Set DeploymentSpec.Replicas to 1 if it is not set.
if obj.Spec.Replicas == nil {
obj.Spec.Replicas = new(int32)
Expand Down Expand Up @@ -158,6 +172,9 @@ func addDeploymentDefaults(objI any) {
// https://github.com/kubernetes/kubernetes/blob/master/pkg/apis/core/v1/defaults.go
func addServiceDefaults(objI any) {
obj := objI.(*corev1.Service)

addDefaultMetadata(obj)

if obj.Spec.SessionAffinity == "" {
obj.Spec.SessionAffinity = corev1.ServiceAffinityNone
}
Expand Down Expand Up @@ -216,6 +233,8 @@ func addVMAuthDefaults(objI any) {
cr := objI.(*vmv1beta1.VMAuth)
c := getCfg()

addDefaultMetadata(cr)

if cr.Spec.ConfigSecret != "" {
// Removed if later with ConfigSecret field later
cr.Spec.SecretRef = &corev1.SecretKeySelector{
Expand All @@ -238,6 +257,8 @@ func addVMAlertDefaults(objI any) {
cr := objI.(*vmv1beta1.VMAlert)
c := getCfg()

addDefaultMetadata(cr)

cv := config.ApplicationDefaults(c.VMAlert)
cp := commonParams{
tag: cr.Spec.ComponentVersion,
Expand All @@ -253,6 +274,7 @@ func addVMAlertDefaults(objI any) {
func addVMAgentDefaults(objI any) {
cr := objI.(*vmv1beta1.VMAgent)
c := getCfg()
addDefaultMetadata(cr)

cv := config.ApplicationDefaults(c.VMAgent)
cp := commonParams{
Expand All @@ -269,6 +291,7 @@ func addVMAgentDefaults(objI any) {
func addVLAgentDefaults(objI any) {
cr := objI.(*vmv1.VLAgent)
c := getCfg()
addDefaultMetadata(cr)

cv := config.ApplicationDefaults(c.VLAgent)
cp := commonParams{
Expand All @@ -281,6 +304,7 @@ func addVLAgentDefaults(objI any) {
func addVMSingleDefaults(objI any) {
cr := objI.(*vmv1beta1.VMSingle)
c := getCfg()
addDefaultMetadata(cr)
cv := config.ApplicationDefaults(c.VMSingle)
cp := commonParams{
tag: cr.Spec.ComponentVersion,
Expand All @@ -302,6 +326,7 @@ func addVMSingleDefaults(objI any) {
func addVLogsDefaults(objI any) {
cr := objI.(*vmv1beta1.VLogs)
c := getCfg()
addDefaultMetadata(cr)
cv := config.ApplicationDefaults(c.VLogs)
cp := commonParams{tag: cr.Spec.ComponentVersion}
addDefaultsToCommonParams(&cr.Spec.CommonAppsParams, &cp, &cv)
Expand All @@ -310,6 +335,8 @@ func addVLogsDefaults(objI any) {
func addVMAnomalyDefaults(objI any) {
cr := objI.(*vmv1.VMAnomaly)

addDefaultMetadata(cr)

// vmanomaly takes up to 2 minutes to start
if cr.Spec.LivenessProbe == nil {
cr.Spec.LivenessProbe = &corev1.Probe{
Expand Down Expand Up @@ -344,6 +371,7 @@ func addVMAnomalyDefaults(objI any) {
func addVLSingleDefaults(objI any) {
cr := objI.(*vmv1.VLSingle)
c := getCfg()
addDefaultMetadata(cr)
cv := config.ApplicationDefaults(c.VLSingle)
cp := commonParams{
tag: cr.Spec.ComponentVersion,
Expand All @@ -355,6 +383,7 @@ func addVLSingleDefaults(objI any) {
func addVTSingleDefaults(objI any) {
cr := objI.(*vmv1.VTSingle)
c := getCfg()
addDefaultMetadata(cr)
cv := config.ApplicationDefaults(c.VTSingle)
cp := commonParams{tag: cr.Spec.ComponentVersion}
addDefaultsToCommonParams(&cr.Spec.CommonAppsParams, &cp, &cv)
Expand All @@ -363,6 +392,7 @@ func addVTSingleDefaults(objI any) {
func addVMAlertmanagerDefaults(objI any) {
cr := objI.(*vmv1beta1.VMAlertmanager)
c := getCfg()
addDefaultMetadata(cr)
cv := config.ApplicationDefaults(c.VMAlertmanager)
if cr.Spec.ClusterDomainName == "" {
cr.Spec.ClusterDomainName = c.ClusterDomainName
Expand Down Expand Up @@ -401,6 +431,7 @@ func addRequestsLoadBalancerDefaults(lb *vmv1beta1.VMAuthLoadBalancer, cp *commo
func addVMClusterDefaults(objI any) {
cr := objI.(*vmv1beta1.VMCluster)
c := getCfg()
addDefaultMetadata(cr)
if cr.Spec.ClusterDomainName == "" {
cr.Spec.ClusterDomainName = c.ClusterDomainName
}
Expand Down Expand Up @@ -454,6 +485,20 @@ func addVMClusterDefaults(objI any) {
}
}

func addDefaultMetadata(objI any) {
cfg := config.MustGetBaseConfig()
obj, ok := objI.(metav1.Object)
if !ok {
return
}
if len(cfg.CommonLabels) > 0 {
obj.SetLabels(labels.Merge(cfg.CommonLabels, obj.GetLabels()))
}
if len(cfg.CommonAnnotations) > 0 {
obj.SetAnnotations(labels.Merge(cfg.CommonAnnotations, obj.GetAnnotations()))
}
}

func addDefaultsToCommonParams(common *vmv1beta1.CommonAppsParams, cp *commonParams, appDefaults *config.ApplicationDefaults) {
c := getCfg()
if common.Image.Repository == "" {
Expand Down Expand Up @@ -524,6 +569,7 @@ func addDefaultsToVMBackup(cr *vmv1beta1.VMBackup, useDefaultResources bool, app
return
}
c := getCfg()
addDefaultMetadata(cr)

if cr.Image.Repository == "" {
cr.Image.Repository = appDefaults.Image
Expand All @@ -549,6 +595,7 @@ func addVMServiceScrapeDefaults(objI any) {
if cr == nil {
return
}
addDefaultMetadata(cr)
c := getCfg()
if cr.Spec.DiscoveryRole == "" && c.VMServiceScrape.EnforceEndpointSlices {
cr.Spec.DiscoveryRole = "endpointslice"
Expand All @@ -563,6 +610,7 @@ const (
func addVTClusterDefaults(objI any) {
cr := objI.(*vmv1.VTCluster)
c := getCfg()
addDefaultMetadata(cr)
cp := commonParams{
useStrictSecurity: cr.Spec.UseStrictSecurity,
tag: cr.Spec.ClusterVersion,
Expand Down Expand Up @@ -606,6 +654,7 @@ func addVTClusterDefaults(objI any) {
func addVLClusterDefaults(objI any) {
cr := objI.(*vmv1.VLCluster)
c := getCfg()
addDefaultMetadata(cr)
cp := commonParams{
useStrictSecurity: cr.Spec.UseStrictSecurity,
tag: cr.Spec.ClusterVersion,
Expand Down
41 changes: 41 additions & 0 deletions internal/controller/operator/factory/build/defaults_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"testing"

"github.com/stretchr/testify/assert"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/utils/ptr"

vmv1 "github.com/VictoriaMetrics/operator/api/operator/v1"
Expand Down Expand Up @@ -549,3 +551,42 @@ func TestClusterComponentVersionDefaults(t *testing.T) {
})
}
}

func TestAddDefaultMetadata(t *testing.T) {
cfg := config.MustGetBaseConfig()
defaultCfg := *cfg
defer func() {
*config.MustGetBaseConfig() = defaultCfg
}()

cfg.CommonLabels = map[string]string{
"common-label": "common-value",
"existing-label": "should-not-overwrite",
}
cfg.CommonAnnotations = map[string]string{
"common-annotation": "common-value",
"existing-annotation": "should-not-overwrite",
}

obj := &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{
"existing-label": "existing-value",
},
Annotations: map[string]string{
"existing-annotation": "existing-value",
},
},
}

addDefaultMetadata(obj)

assert.Equal(t, map[string]string{
"common-label": "common-value",
"existing-label": "existing-value",
}, obj.Labels)
assert.Equal(t, map[string]string{
"common-annotation": "common-value",
"existing-annotation": "existing-value",
}, obj.Annotations)
}
Loading
Loading