diff --git a/api/operator/v1beta1/common_scrapeparams.go b/api/operator/v1beta1/common_scrapeparams.go index 0e4374b9d..edd4d6a0e 100644 --- a/api/operator/v1beta1/common_scrapeparams.go +++ b/api/operator/v1beta1/common_scrapeparams.go @@ -8,6 +8,7 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" ) // AttachMetadata configures metadata attachment @@ -358,7 +359,7 @@ type CommonScrapeSecurityEnforcements struct { EnforcedNamespaceLabel string `json:"enforcedNamespaceLabel,omitempty"` // ArbitraryFSAccessThroughSMs configures whether configuration // based on EndpointAuth can access arbitrary files on the file system - // of the VMAgent container e.g. bearer token files, basic auth, tls certs + // of the VMAgent or VMSingle container e.g. bearer token files, basic auth, tls certs // +optional ArbitraryFSAccessThroughSMs ArbitraryFSAccessThroughSMsConfig `json:"arbitraryFSAccessThroughSMs,omitempty"` } @@ -389,56 +390,56 @@ type CommonScrapeParams struct { SelectAllByDefault bool `json:"selectAllByDefault,omitempty"` // ServiceScrapeSelector defines ServiceScrapes to be selected for target discovery. // Works in combination with NamespaceSelector. - // NamespaceSelector nil - only objects at VMAgent namespace. + // NamespaceSelector nil - only objects at VMAgent or VMSingle namespace. // Selector nil - only objects at NamespaceSelector namespaces. // If both nil - behaviour controlled by selectAllByDefault // +optional ServiceScrapeSelector *metav1.LabelSelector `json:"serviceScrapeSelector,omitempty"` // ServiceScrapeNamespaceSelector Namespaces to be selected for VMServiceScrape discovery. // Works in combination with Selector. - // NamespaceSelector nil - only objects at VMAgent namespace. + // NamespaceSelector nil - only objects at VMAgent or VMSingle namespace. // Selector nil - only objects at NamespaceSelector namespaces. // If both nil - behaviour controlled by selectAllByDefault // +optional ServiceScrapeNamespaceSelector *metav1.LabelSelector `json:"serviceScrapeNamespaceSelector,omitempty"` // PodScrapeSelector defines PodScrapes to be selected for target discovery. // Works in combination with NamespaceSelector. - // NamespaceSelector nil - only objects at VMAgent namespace. + // NamespaceSelector nil - only objects at VMAgent or VMSingle namespace. // Selector nil - only objects at NamespaceSelector namespaces. // If both nil - behaviour controlled by selectAllByDefault // +optional PodScrapeSelector *metav1.LabelSelector `json:"podScrapeSelector,omitempty"` // PodScrapeNamespaceSelector defines Namespaces to be selected for VMPodScrape discovery. // Works in combination with Selector. - // NamespaceSelector nil - only objects at VMAgent namespace. + // NamespaceSelector nil - only objects at VMAgent or VMSingle namespace. // Selector nil - only objects at NamespaceSelector namespaces. // If both nil - behaviour controlled by selectAllByDefault // +optional PodScrapeNamespaceSelector *metav1.LabelSelector `json:"podScrapeNamespaceSelector,omitempty"` // ProbeSelector defines VMProbe to be selected for target probing. // Works in combination with NamespaceSelector. - // NamespaceSelector nil - only objects at VMAgent namespace. + // NamespaceSelector nil - only objects at VMAgent or VMSingle namespace. // Selector nil - only objects at NamespaceSelector namespaces. // If both nil - behaviour controlled by selectAllByDefault // +optional ProbeSelector *metav1.LabelSelector `json:"probeSelector,omitempty"` // ProbeNamespaceSelector defines Namespaces to be selected for VMProbe discovery. // Works in combination with Selector. - // NamespaceSelector nil - only objects at VMAgent namespace. + // NamespaceSelector nil - only objects at VMAgent or VMSingle namespace. // Selector nil - only objects at NamespaceSelector namespaces. // If both nil - behaviour controlled by selectAllByDefault // +optional ProbeNamespaceSelector *metav1.LabelSelector `json:"probeNamespaceSelector,omitempty"` // NodeScrapeSelector defines VMNodeScrape to be selected for scraping. // Works in combination with NamespaceSelector. - // NamespaceSelector nil - only objects at VMAgent namespace. + // NamespaceSelector nil - only objects at VMAgent or VMSingle namespace. // Selector nil - only objects at NamespaceSelector namespaces. // If both nil - behaviour controlled by selectAllByDefault // +optional NodeScrapeSelector *metav1.LabelSelector `json:"nodeScrapeSelector,omitempty"` // NodeScrapeNamespaceSelector defines Namespaces to be selected for VMNodeScrape discovery. // Works in combination with Selector. - // NamespaceSelector nil - only objects at VMAgent namespace. + // NamespaceSelector nil - only objects at VMAgent or VMSingle namespace. // Selector nil - only objects at NamespaceSelector namespaces. // If both nil - behaviour controlled by selectAllByDefault // +optional @@ -446,13 +447,13 @@ type CommonScrapeParams struct { // StaticScrapeSelector defines VMStaticScrape to be selected for target discovery. // Works in combination with NamespaceSelector. // If both nil - match everything. - // NamespaceSelector nil - only objects at VMAgent namespace. + // NamespaceSelector nil - only objects at VMAgent or VMSingle namespace. // Selector nil - only objects at NamespaceSelector namespaces. // +optional StaticScrapeSelector *metav1.LabelSelector `json:"staticScrapeSelector,omitempty"` // StaticScrapeNamespaceSelector defines Namespaces to be selected for VMStaticScrape discovery. // Works in combination with NamespaceSelector. - // NamespaceSelector nil - only objects at VMAgent namespace. + // NamespaceSelector nil - only objects at VMAgent or VMSingle namespace. // Selector nil - only objects at NamespaceSelector namespaces. // If both nil - behaviour controlled by selectAllByDefault // +optional @@ -463,16 +464,16 @@ type CommonScrapeParams struct { ScrapeConfigSelector *metav1.LabelSelector `json:"scrapeConfigSelector,omitempty"` // ScrapeConfigNamespaceSelector defines Namespaces to be selected for VMScrapeConfig discovery. // Works in combination with Selector. - // NamespaceSelector nil - only objects at VMAgent namespace. + // NamespaceSelector nil - only objects at VMAgent or VMSingle namespace. // Selector nil - only objects at NamespaceSelector namespaces. // If both nil - behaviour controlled by selectAllByDefault // +optional ScrapeConfigNamespaceSelector *metav1.LabelSelector `json:"scrapeConfigNamespaceSelector,omitempty"` // 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 + // break upgrades of VMAgent or VMSingle. It is advised to review VMAgent or VMSingle release // notes to ensure that no incompatible scrape configs are going to break - // VMAgent after the upgrade. + // VMAgent or VMSingle after the upgrade. // it should be defined as single yaml file. // inlineScrapeConfig: | // - job_name: "prometheus" @@ -482,9 +483,9 @@ type CommonScrapeParams struct { InlineScrapeConfig string `json:"inlineScrapeConfig,omitempty"` // AdditionalScrapeConfigs 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 + // break upgrades of VMAgent or VMSingle. It is advised to review VMAgent or VMSingle release // notes to ensure that no incompatible scrape configs are going to break - // VMAgent after the upgrade. + // VMAgent or VMSingle after the upgrade. // +optional AdditionalScrapeConfigs *corev1.SecretKeySelector `json:"additionalScrapeConfigs,omitempty"` // ServiceScrapeRelabelTemplate defines relabel config, that will be added to each VMServiceScrape. @@ -539,6 +540,11 @@ type CommonScrapeParams struct { // it doesn't affect metrics ingested directly by push API's // +optional ExternalLabels map[string]string `json:"externalLabels,omitempty"` + // IngestOnlyMode switches vmagent or vmsingle into unmanaged mode + // it disables any config generation for scraping + // Currently it prevents vmagent from managing tls and auth options for remote write + // +optional + IngestOnlyMode *bool `json:"ingestOnlyMode,omitempty"` // EnableKubernetesAPISelectors instructs vmagent to use CRD scrape objects spec.selectors for // Kubernetes API list and watch requests. // https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#list-and-watch-filtering @@ -550,6 +556,9 @@ type CommonScrapeParams struct { // isUnmanaged checks if object should managed any config objects func (cr *CommonScrapeParams) isUnmanaged() bool { + if ptr.Deref(cr.IngestOnlyMode, false) { + return true + } return !cr.SelectAllByDefault && cr.NodeScrapeSelector == nil && cr.NodeScrapeNamespaceSelector == nil && cr.ServiceScrapeSelector == nil && cr.ServiceScrapeNamespaceSelector == nil && @@ -561,36 +570,54 @@ func (cr *CommonScrapeParams) isUnmanaged() bool { // isNodeScrapeUnmanaged checks if scraping agent should managed any VMNodeScrape objects func (cr *CommonScrapeParams) isNodeScrapeUnmanaged() bool { + if ptr.Deref(cr.IngestOnlyMode, false) { + return true + } return !cr.SelectAllByDefault && cr.NodeScrapeSelector == nil && cr.NodeScrapeNamespaceSelector == nil } // isServiceScrapeUnmanaged checks if scraping agent should managed any VMServiceScrape objects func (cr *CommonScrapeParams) isServiceScrapeUnmanaged() bool { + if ptr.Deref(cr.IngestOnlyMode, false) { + return true + } return !cr.SelectAllByDefault && cr.ServiceScrapeSelector == nil && cr.ServiceScrapeNamespaceSelector == nil } // isUnmanaged checks if scraping agent should managed any VMPodScrape objects func (cr *CommonScrapeParams) isPodScrapeUnmanaged() bool { + if ptr.Deref(cr.IngestOnlyMode, false) { + return true + } return !cr.SelectAllByDefault && cr.PodScrapeSelector == nil && cr.PodScrapeNamespaceSelector == nil } // isProbeUnmanaged checks if scraping agent should managed any VMProbe objects func (cr *CommonScrapeParams) isProbeUnmanaged() bool { + if ptr.Deref(cr.IngestOnlyMode, false) { + return true + } return !cr.SelectAllByDefault && cr.ProbeSelector == nil && cr.ProbeNamespaceSelector == nil } // isStaticScrapeUnmanaged checks if scraping agent should managed any VMStaticScrape objects func (cr *CommonScrapeParams) isStaticScrapeUnmanaged() bool { + if ptr.Deref(cr.IngestOnlyMode, false) { + return true + } return !cr.SelectAllByDefault && cr.StaticScrapeSelector == nil && cr.StaticScrapeNamespaceSelector == nil } // isScrapeConfigUnmanaged checks if scraping agent should managed any VMScrapeConfig objects func (cr *CommonScrapeParams) isScrapeConfigUnmanaged() bool { + if ptr.Deref(cr.IngestOnlyMode, false) { + return true + } return !cr.SelectAllByDefault && cr.ScrapeConfigSelector == nil && cr.ScrapeConfigNamespaceSelector == nil } diff --git a/api/operator/v1beta1/owner.go b/api/operator/v1beta1/owner.go index b669ea7e7..87cf2582c 100644 --- a/api/operator/v1beta1/owner.go +++ b/api/operator/v1beta1/owner.go @@ -15,12 +15,14 @@ type CRDName int const ( VMAgentCRD CRDName = iota VLAgentCRD + VMSingleCRD ) func (c CRDName) String() string { return []string{ "vmagents.operator.victoriametrics.com", "vlagents.operator.victoriametrics.com", + "vmsingles.operator.victoriametrics.com", }[c] } @@ -46,6 +48,8 @@ func Init(ctx context.Context, rclient client.Client) error { n = VMAgentCRD case "vlagents.operator.victoriametrics.com": n = VLAgentCRD + case "vmsingles.operator.victoriametrics.com": + n = VMSingleCRD default: continue } diff --git a/api/operator/v1beta1/vmagent_types.go b/api/operator/v1beta1/vmagent_types.go index fe8af8899..56ecde847 100644 --- a/api/operator/v1beta1/vmagent_types.go +++ b/api/operator/v1beta1/vmagent_types.go @@ -34,14 +34,12 @@ type VMAgentSpec struct { // +optional // +kubebuilder:validation:Enum=default;json LogFormat string `json:"logFormat,omitempty"` - // APIServerConfig allows specifying a host and auth methods to access apiserver. // If left empty, VMAgent is assumed to run inside of the cluster // and will discover API servers automatically and use the pod's CA certificate // and bearer token file at /var/run/secrets/kubernetes.io/serviceaccount/. // +optional APIServerConfig *APIServerConfig `json:"apiServerConfig,omitempty"` - // 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 @@ -105,11 +103,6 @@ type VMAgentSpec struct { // ClaimTemplates allows adding additional VolumeClaimTemplates for VMAgent in StatefulMode ClaimTemplates []corev1.PersistentVolumeClaim `json:"claimTemplates,omitempty"` - // IngestOnlyMode switches vmagent into unmanaged mode - // it disables any config generation for scraping - // Currently it prevents vmagent from managing tls and auth options for remote write - // +optional - IngestOnlyMode bool `json:"ingestOnlyMode,omitempty"` // License allows to configure license key to be used for enterprise features. // Using license key is supported starting from VictoriaMetrics v1.94.0. @@ -350,7 +343,7 @@ type VMAgentRemoteWriteSpec struct { // +optional BearerTokenSecret *corev1.SecretKeySelector `json:"bearerTokenSecret,omitempty"` - // ConfigMap with relabeling config which is applied to metrics before sending them to the corresponding -remoteWrite.url + // ConfigMap with relabeling config which is applied to metrics before sending them to the corresponding -remoteWrite.url. // +optional // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Key at Configmap with relabelConfig for remoteWrite",xDescriptors="urn:alm:descriptor:io.kubernetes:ConfigMapKeySelector" UrlRelabelConfig *corev1.ConfigMapKeySelector `json:"urlRelabelConfig,omitempty"` @@ -567,7 +560,7 @@ func (cr *VMAgent) IsOwnsServiceAccount() bool { } func (cr *VMAgent) GetClusterRoleName() string { - return fmt.Sprintf("monitoring:%s:vmagent-%s", cr.Namespace, cr.Name) + return fmt.Sprintf("monitoring:%s:%s", cr.Namespace, cr.PrefixedName()) } // AsURL - returns url for http access @@ -614,64 +607,36 @@ func (*VMAgent) ProbeNeedLiveness() bool { // IsUnmanaged checks if object should managed any config objects func (cr *VMAgent) IsUnmanaged() bool { - // fast path - if cr.Spec.IngestOnlyMode { - return true - } return cr.Spec.isUnmanaged() } // IsNodeScrapeUnmanaged checks if vmagent should managed any VMNodeScrape objects func (cr *VMAgent) IsNodeScrapeUnmanaged() bool { - // fast path - if cr.Spec.IngestOnlyMode { - return true - } return cr.Spec.isNodeScrapeUnmanaged() } // IsServiceScrapeUnmanaged checks if vmagent should managed any VMServiceScrape objects func (cr *VMAgent) IsServiceScrapeUnmanaged() bool { - // fast path - if cr.Spec.IngestOnlyMode { - return true - } return cr.Spec.isServiceScrapeUnmanaged() } // IsUnmanaged checks if vmagent should managed any VMPodScrape objects func (cr *VMAgent) IsPodScrapeUnmanaged() bool { - // fast path - if cr.Spec.IngestOnlyMode { - return true - } return cr.Spec.isPodScrapeUnmanaged() } // IsProbeUnmanaged checks if vmagent should managed any VMProbe objects func (cr *VMAgent) IsProbeUnmanaged() bool { - // fast path - if cr.Spec.IngestOnlyMode { - return true - } return cr.Spec.isProbeUnmanaged() } // IsStaticScrapeUnmanaged checks if vmagent should managed any VMStaticScrape objects func (cr *VMAgent) IsStaticScrapeUnmanaged() bool { - // fast path - if cr.Spec.IngestOnlyMode { - return true - } return cr.Spec.isStaticScrapeUnmanaged() } // IsScrapeConfigUnmanaged checks if vmagent should managed any VMScrapeConfig objects func (cr *VMAgent) IsScrapeConfigUnmanaged() bool { - // fast path - if cr.Spec.IngestOnlyMode { - return true - } return cr.Spec.isScrapeConfigUnmanaged() } diff --git a/api/operator/v1beta1/vmextra_types.go b/api/operator/v1beta1/vmextra_types.go index efb2d4cc4..e7a000855 100644 --- a/api/operator/v1beta1/vmextra_types.go +++ b/api/operator/v1beta1/vmextra_types.go @@ -66,6 +66,7 @@ const ( LastAppliedSpecAnnotation = "operator.victoriametrics/last-applied-spec" VMAuthLBServiceProxyTargetLabel = "operator.victoriametrics.com/vmauthlb-proxy-name" VMAuthLBServiceProxyJobNameLabel = "operator.victoriametrics.com/vmauthlb-proxy-job-name" + KubeNodeEnvName = "KUBE_NODE_NAME" ) const ( diff --git a/api/operator/v1beta1/vmsingle_types.go b/api/operator/v1beta1/vmsingle_types.go index 5f4aa2a93..c86a919a7 100644 --- a/api/operator/v1beta1/vmsingle_types.go +++ b/api/operator/v1beta1/vmsingle_types.go @@ -79,12 +79,21 @@ type VMSingleSpec struct { *EmbeddedProbes `json:",inline"` // StreamAggrConfig defines stream aggregation configuration for VMSingle StreamAggrConfig *StreamAggrConfig `json:"streamAggrConfig,omitempty"` + // APIServerConfig allows specifying a host and auth methods to access apiserver. + // If left empty, VMSingle is assumed to run inside of the cluster + // and will discover API servers automatically and use the pod's CA certificate + // and bearer token file at /var/run/secrets/kubernetes.io/serviceaccount/. + // +optional + APIServerConfig *APIServerConfig `json:"apiServerConfig,omitempty"` // ServiceAccountName is the name of the ServiceAccount to use to run the pods // +optional ServiceAccountName string `json:"serviceAccountName,omitempty"` + CommonRelabelParams `json:",inline,omitempty"` + CommonScrapeParams `json:",inline,omitempty"` CommonDefaultableParams `json:",inline"` + CommonConfigReloaderParams `json:",inline,omitempty"` CommonApplicationDeploymentParams `json:",inline"` } @@ -93,11 +102,74 @@ func (cr *VMSingle) HasAnyStreamAggrRule() bool { return cr.Spec.StreamAggrConfig.HasAnyRule() } +// HasAnyRelabellingConfigs checks if vmagent has any defined relabeling rules +func (cr *VMSingle) HasAnyRelabellingConfigs() bool { + return cr.Spec.HasAnyRelabellingConfigs() +} + // SetLastSpec implements objectWithLastAppliedState interface func (cr *VMSingle) SetLastSpec(prevSpec VMSingleSpec) { cr.ParsedLastAppliedSpec = &prevSpec } +// IsUnmanaged checks if object should managed any config objects +func (cr *VMSingle) IsUnmanaged() bool { + return cr.Spec.isUnmanaged() +} + +// IsNodeScrapeUnmanaged checks if vmagent should managed any VMNodeScrape objects +func (cr *VMSingle) IsNodeScrapeUnmanaged() bool { + return cr.Spec.isNodeScrapeUnmanaged() +} + +// IsServiceScrapeUnmanaged checks if vmagent should managed any VMServiceScrape objects +func (cr *VMSingle) IsServiceScrapeUnmanaged() bool { + return cr.Spec.isServiceScrapeUnmanaged() +} + +// IsUnmanaged checks if vmagent should managed any VMPodScrape objects +func (cr *VMSingle) IsPodScrapeUnmanaged() bool { + return cr.Spec.isPodScrapeUnmanaged() +} + +// IsProbeUnmanaged checks if vmagent should managed any VMProbe objects +func (cr *VMSingle) IsProbeUnmanaged() bool { + return cr.Spec.isProbeUnmanaged() +} + +// IsStaticScrapeUnmanaged checks if vmagent should managed any VMStaticScrape objects +func (cr *VMSingle) IsStaticScrapeUnmanaged() bool { + return cr.Spec.isStaticScrapeUnmanaged() +} + +// IsScrapeConfigUnmanaged checks if vmagent should managed any VMScrapeConfig objects +func (cr *VMSingle) IsScrapeConfigUnmanaged() bool { + return cr.Spec.isScrapeConfigUnmanaged() +} + +// GetReloadURL implements reloadable interface +func (cr *VMSingle) GetReloadURL() string { + return BuildReloadPathWithPort(cr.Spec.ExtraArgs, cr.Spec.Port) +} + +// GetReloaderParams implements reloadable interface +func (cr *VMSingle) GetReloaderParams() *CommonConfigReloaderParams { + return &cr.Spec.CommonConfigReloaderParams +} + +// UseProxyProtocol implements reloadable interface +func (cr *VMSingle) UseProxyProtocol() bool { + if v, ok := cr.Spec.ExtraArgs["httpListenAddr.useProxyProtocol"]; ok && v == "true" { + return true + } + return false +} + +// AutomountServiceAccountToken implements reloadable interface +func (cr *VMSingle) AutomountServiceAccountToken() bool { + return !cr.Spec.DisableAutomountServiceAccountToken +} + // UnmarshalJSON implements json.Unmarshaler interface func (cr *VMSingle) UnmarshalJSON(src []byte) error { type pcr VMSingle @@ -151,6 +223,11 @@ type VMSingle struct { Status VMSingleStatus `json:"status,omitempty"` } +// AsCRDOwner implements interface +func (*VMSingle) AsCRDOwner() *metav1.OwnerReference { + return GetCRDAsOwner(VMSingleCRD) +} + // GetStatus implements reconcile.ObjectWithDeepCopyAndStatus interface func (cr *VMSingle) GetStatus() *VMSingleStatus { return &cr.Status @@ -275,6 +352,10 @@ func (cr *VMSingle) IsOwnsServiceAccount() bool { return cr.Spec.ServiceAccountName == "" } +func (cr *VMSingle) GetClusterRoleName() string { + return fmt.Sprintf("monitoring:%s:%s", cr.Namespace, cr.PrefixedName()) +} + func (cr *VMSingle) AsURL() string { port := cr.Spec.Port if port == "" { diff --git a/api/operator/v1beta1/zz_generated.deepcopy.go b/api/operator/v1beta1/zz_generated.deepcopy.go index 9bfedbdde..e3006176d 100644 --- a/api/operator/v1beta1/zz_generated.deepcopy.go +++ b/api/operator/v1beta1/zz_generated.deepcopy.go @@ -801,6 +801,11 @@ func (in *CommonScrapeParams) DeepCopyInto(out *CommonScrapeParams) { (*out)[key] = val } } + if in.IngestOnlyMode != nil { + in, out := &in.IngestOnlyMode, &out.IngestOnlyMode + *out = new(bool) + **out = **in + } out.CommonScrapeSecurityEnforcements = in.CommonScrapeSecurityEnforcements } @@ -6632,7 +6637,15 @@ func (in *VMSingleSpec) DeepCopyInto(out *VMSingleSpec) { *out = new(StreamAggrConfig) (*in).DeepCopyInto(*out) } + if in.APIServerConfig != nil { + in, out := &in.APIServerConfig, &out.APIServerConfig + *out = new(APIServerConfig) + (*in).DeepCopyInto(*out) + } + 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) } diff --git a/config/crd/overlay/crd.yaml b/config/crd/overlay/crd.yaml index a82805589..c23b5bea9 100644 --- a/config/crd/overlay/crd.yaml +++ b/config/crd/overlay/crd.yaml @@ -7177,9 +7177,9 @@ spec: description: |- AdditionalScrapeConfigs 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 + break upgrades of VMAgent or VMSingle. It is advised to review VMAgent or VMSingle release notes to ensure that no incompatible scrape configs are going to break - VMAgent after the upgrade. + VMAgent or VMSingle after the upgrade. properties: key: description: The key of the secret to select from. Must be a @@ -7479,7 +7479,7 @@ spec: description: |- ArbitraryFSAccessThroughSMs configures whether configuration based on EndpointAuth can access arbitrary files on the file system - of the VMAgent container e.g. bearer token files, basic auth, tls certs + of the VMAgent or VMSingle container e.g. bearer token files, basic auth, tls certs properties: deny: type: boolean @@ -8451,7 +8451,7 @@ spec: type: array ingestOnlyMode: description: |- - IngestOnlyMode switches vmagent into unmanaged mode + IngestOnlyMode switches vmagent or vmsingle into unmanaged mode it disables any config generation for scraping Currently it prevents vmagent from managing tls and auth options for remote write type: boolean @@ -8549,9 +8549,9 @@ spec: description: |- 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 + break upgrades of VMAgent or VMSingle. It is advised to review VMAgent or VMSingle release notes to ensure that no incompatible scrape configs are going to break - VMAgent after the upgrade. + VMAgent or VMSingle after the upgrade. it should be defined as single yaml file. inlineScrapeConfig: | - job_name: "prometheus" @@ -8683,7 +8683,7 @@ spec: description: |- NodeScrapeNamespaceSelector defines Namespaces to be selected for VMNodeScrape discovery. Works in combination with Selector. - NamespaceSelector nil - only objects at VMAgent namespace. + NamespaceSelector nil - only objects at VMAgent or VMSingle namespace. Selector nil - only objects at NamespaceSelector namespaces. If both nil - behaviour controlled by selectAllByDefault properties: @@ -8812,7 +8812,7 @@ spec: description: |- NodeScrapeSelector defines VMNodeScrape to be selected for scraping. Works in combination with NamespaceSelector. - NamespaceSelector nil - only objects at VMAgent namespace. + NamespaceSelector nil - only objects at VMAgent or VMSingle namespace. Selector nil - only objects at NamespaceSelector namespaces. If both nil - behaviour controlled by selectAllByDefault properties: @@ -8978,7 +8978,7 @@ spec: description: |- PodScrapeNamespaceSelector defines Namespaces to be selected for VMPodScrape discovery. Works in combination with Selector. - NamespaceSelector nil - only objects at VMAgent namespace. + NamespaceSelector nil - only objects at VMAgent or VMSingle namespace. Selector nil - only objects at NamespaceSelector namespaces. If both nil - behaviour controlled by selectAllByDefault properties: @@ -9107,7 +9107,7 @@ spec: description: |- PodScrapeSelector defines PodScrapes to be selected for target discovery. Works in combination with NamespaceSelector. - NamespaceSelector nil - only objects at VMAgent namespace. + NamespaceSelector nil - only objects at VMAgent or VMSingle namespace. Selector nil - only objects at NamespaceSelector namespaces. If both nil - behaviour controlled by selectAllByDefault properties: @@ -9164,7 +9164,7 @@ spec: description: |- ProbeNamespaceSelector defines Namespaces to be selected for VMProbe discovery. Works in combination with Selector. - NamespaceSelector nil - only objects at VMAgent namespace. + NamespaceSelector nil - only objects at VMAgent or VMSingle namespace. Selector nil - only objects at NamespaceSelector namespaces. If both nil - behaviour controlled by selectAllByDefault properties: @@ -9293,7 +9293,7 @@ spec: description: |- ProbeSelector defines VMProbe to be selected for target probing. Works in combination with NamespaceSelector. - NamespaceSelector nil - only objects at VMAgent namespace. + NamespaceSelector nil - only objects at VMAgent or VMSingle namespace. Selector nil - only objects at NamespaceSelector namespaces. If both nil - behaviour controlled by selectAllByDefault properties: @@ -10225,7 +10225,7 @@ spec: type: string urlRelabelConfig: description: ConfigMap with relabeling config which is applied - to metrics before sending them to the corresponding -remoteWrite.url + to metrics before sending them to the corresponding -remoteWrite.url. properties: key: description: The key to select. @@ -11011,7 +11011,7 @@ spec: description: |- ScrapeConfigNamespaceSelector defines Namespaces to be selected for VMScrapeConfig discovery. Works in combination with Selector. - NamespaceSelector nil - only objects at VMAgent namespace. + NamespaceSelector nil - only objects at VMAgent or VMSingle namespace. Selector nil - only objects at NamespaceSelector namespaces. If both nil - behaviour controlled by selectAllByDefault properties: @@ -11221,7 +11221,7 @@ spec: description: |- ServiceScrapeNamespaceSelector Namespaces to be selected for VMServiceScrape discovery. Works in combination with Selector. - NamespaceSelector nil - only objects at VMAgent namespace. + NamespaceSelector nil - only objects at VMAgent or VMSingle namespace. Selector nil - only objects at NamespaceSelector namespaces. If both nil - behaviour controlled by selectAllByDefault properties: @@ -11350,7 +11350,7 @@ spec: description: |- ServiceScrapeSelector defines ServiceScrapes to be selected for target discovery. Works in combination with NamespaceSelector. - NamespaceSelector nil - only objects at VMAgent namespace. + NamespaceSelector nil - only objects at VMAgent or VMSingle namespace. Selector nil - only objects at NamespaceSelector namespaces. If both nil - behaviour controlled by selectAllByDefault properties: @@ -11946,7 +11946,7 @@ spec: description: |- StaticScrapeNamespaceSelector defines Namespaces to be selected for VMStaticScrape discovery. Works in combination with NamespaceSelector. - NamespaceSelector nil - only objects at VMAgent namespace. + NamespaceSelector nil - only objects at VMAgent or VMSingle namespace. Selector nil - only objects at NamespaceSelector namespaces. If both nil - behaviour controlled by selectAllByDefault properties: @@ -12076,7 +12076,7 @@ spec: StaticScrapeSelector defines VMStaticScrape to be selected for target discovery. Works in combination with NamespaceSelector. If both nil - match everything. - NamespaceSelector nil - only objects at VMAgent namespace. + NamespaceSelector nil - only objects at VMAgent or VMSingle namespace. Selector nil - only objects at NamespaceSelector namespaces. properties: matchExpressions: @@ -39312,10 +39312,317 @@ spec: spec: description: VMSingleSpec defines the desired state of VMSingle properties: + additionalScrapeConfigs: + description: |- + AdditionalScrapeConfigs 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 or VMSingle. It is advised to review VMAgent or VMSingle release + notes to ensure that no incompatible scrape configs are going to break + VMAgent or VMSingle after the upgrade. + properties: + key: + description: The key of the secret to select from. Must be a + valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic affinity: description: Affinity If specified, the pod's scheduling constraints. type: object x-kubernetes-preserve-unknown-fields: true + apiServerConfig: + description: |- + APIServerConfig allows specifying a host and auth methods to access apiserver. + If left empty, VMSingle is assumed to run inside of the cluster + and will discover API servers automatically and use the pod's CA certificate + and bearer token file at /var/run/secrets/kubernetes.io/serviceaccount/. + properties: + authorization: + description: Authorization configures generic authorization params + properties: + credentials: + description: Reference to the secret with value for authorization + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + credentialsFile: + description: File with value for authorization + type: string + type: + description: Type of authorization, default to bearer + type: string + type: object + basicAuth: + description: BasicAuth allow an endpoint to authenticate over + basic authentication + properties: + password: + description: |- + Password defines reference for secret with password value + The secret needs to be in the same namespace as scrape object + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + password_file: + description: |- + PasswordFile defines path to password file at disk + must be pre-mounted + type: string + username: + description: |- + Username defines reference for secret with username value + The secret needs to be in the same namespace as scrape object + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + bearerToken: + description: Bearer token for accessing apiserver. + type: string + bearerTokenFile: + description: File to read bearer token for accessing apiserver. + type: string + host: + description: |- + Host of apiserver. + A valid string consisting of a hostname or IP followed by an optional port number + type: string + tlsConfig: + description: TLSConfig Config to use for accessing apiserver. + properties: + ca: + description: Struct containing the CA cert to use for the + targets. + properties: + configMap: + description: ConfigMap containing data to use for the + targets. + properties: + key: + description: The key to select. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the ConfigMap or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + description: Secret containing data to use for the targets. + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + caFile: + description: Path to the CA cert in the container to use for + the targets. + type: string + cert: + description: Struct containing the client cert file for the + targets. + properties: + configMap: + description: ConfigMap containing data to use for the + targets. + properties: + key: + description: The key to select. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the ConfigMap or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + description: Secret containing data to use for the targets. + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + certFile: + description: Path to the client cert file in the container + for the targets. + type: string + insecureSkipVerify: + description: Disable target certificate validation. + type: boolean + keyFile: + description: Path to the client key file in the container + for the targets. + type: string + keySecret: + description: Secret containing the client key file for the + targets. + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + serverName: + description: Used to verify the hostname for the targets. + type: string + type: object + required: + - host + type: object + arbitraryFSAccessThroughSMs: + description: |- + ArbitraryFSAccessThroughSMs configures whether configuration + based on EndpointAuth can access arbitrary files on the file system + of the VMAgent or VMSingle container e.g. bearer token files, basic auth, tls certs + properties: + deny: + type: boolean + type: object configMaps: description: |- ConfigMaps is a list of ConfigMaps in the same namespace as the Application @@ -39324,6 +39631,109 @@ spec: items: type: string type: array + configReloadAuthKeySecret: + description: |- + 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 + properties: + key: + description: The key of the secret to select from. Must be a + valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + configReloaderExtraArgs: + additionalProperties: + type: string + description: |- + ConfigReloaderExtraArgs that will be passed to VMAuths config-reloader container + for example resyncInterval: "30s" + type: object + configReloaderImage: + description: ConfigReloaderImage defines image:tag for config-reloader + container + type: string + configReloaderImageTag: + description: |- + ConfigReloaderImageTag defines image:tag for config-reloader container + Deprecated: use configReloaderImage instead + type: string + configReloaderResources: + 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 + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This field depends on the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object containers: description: |- Containers property allows to inject additions sidecars or to patch existing containers. @@ -39401,6 +39811,32 @@ spec: dnsPolicy: description: DNSPolicy sets DNS policy for the pod type: string + enableKubernetesAPISelectors: + description: |- + EnableKubernetesAPISelectors instructs vmagent to use CRD scrape objects spec.selectors for + Kubernetes API list and watch requests. + https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#list-and-watch-filtering + It could be useful to reduce Kubernetes API server resource usage for serving less than 100 CRD scrape objects in total. + type: boolean + enforcedNamespaceLabel: + description: |- + EnforcedNamespaceLabel enforces adding a namespace label of origin for each alert + and metric that is user created. The label value will always be the namespace of the object that is + being created. + type: string + externalLabelName: + description: |- + ExternalLabelName Name of external label used to denote scraping agent instance + name. Defaults to the value of `prometheus`. External label will + _not_ be added when value is set to empty string (`""`). + type: string + externalLabels: + additionalProperties: + type: string + description: |- + ExternalLabels The labels to add to any time series scraped by vmagent. + it doesn't affect metrics ingested directly by push API's + type: object extraArgs: additionalProperties: type: string @@ -39485,47 +39921,202 @@ spec: x-kubernetes-map-type: atomic type: object type: array - host_aliases: - description: |- - HostAliasesUnderScore provides mapping for ip and hostname, - that would be propagated to pod, - cannot be used with HostNetwork. - Has Priority over hostAliases field + globalScrapeMetricRelabelConfigs: + description: GlobalScrapeMetricRelabelConfigs is a global metric relabel + configuration, which is applied to each scrape job. items: description: |- - HostAlias holds the mapping between IP and hostnames that will be injected as an entry in the - pod's hosts file. + RelabelConfig allows dynamic rewriting of the label set + More info: https://docs.victoriametrics.com/victoriametrics/#relabeling properties: - hostnames: - description: Hostnames for the above IP address. - items: + action: + description: Action to perform based on regex matching. Default + is 'replace' + type: string + if: + description: 'If represents metricsQL match expression (or list + of expressions): ''{__name__=~"foo_.*"}''' + x-kubernetes-preserve-unknown-fields: true + labels: + additionalProperties: type: string - type: array - x-kubernetes-list-type: atomic - ip: - description: IP address of the host file entry. + description: 'Labels is used together with Match for `action: + graphite`' + type: object + match: + description: 'Match is used together with Labels for `action: + graphite`' type: string - required: - - ip - type: object - type: array - hostAliases: - description: |- - HostAliases provides mapping for ip and hostname, - that would be propagated to pod, - cannot be used with HostNetwork. - items: - description: |- - HostAlias holds the mapping between IP and hostnames that will be injected as an entry in the - pod's hosts file. - properties: - hostnames: - description: Hostnames for the above IP address. + modulus: + description: Modulus to take of the hash of the source label + values. + format: int64 + type: integer + regex: + description: |- + Regular expression against which the extracted value is matched. Default is '(.*)' + victoriaMetrics supports multiline regex joined with | + https://docs.victoriametrics.com/victoriametrics/vmagent/#relabeling-enhancements + x-kubernetes-preserve-unknown-fields: true + replacement: + description: |- + Replacement value against which a regex replace is performed if the + regular expression matches. Regex capture groups are available. Default is '$1' + type: string + separator: + description: Separator placed between concatenated source label + values. default is ';'. + type: string + source_labels: + description: |- + UnderScoreSourceLabels - additional form of source labels source_labels + for compatibility with original relabel config. + if set both sourceLabels and source_labels, sourceLabels has priority. + for details https://github.com/VictoriaMetrics/operator/issues/131 items: type: string type: array - x-kubernetes-list-type: atomic - ip: + sourceLabels: + description: |- + The source labels select values from existing labels. Their content is concatenated + using the configured separator and matched against the configured regular expression + for the replace, keep, and drop actions. + items: + type: string + type: array + target_label: + description: |- + UnderScoreTargetLabel - additional form of target label - target_label + for compatibility with original relabel config. + if set both targetLabel and target_label, targetLabel has priority. + for details https://github.com/VictoriaMetrics/operator/issues/131 + type: string + targetLabel: + description: |- + Label to which the resulting value is written in a replace action. + It is mandatory for replace actions. Regex capture groups are available. + type: string + type: object + type: array + globalScrapeRelabelConfigs: + description: GlobalScrapeRelabelConfigs is a global relabel configuration, + which is applied to each samples of each scrape job during service + discovery. + items: + description: |- + RelabelConfig allows dynamic rewriting of the label set + More info: https://docs.victoriametrics.com/victoriametrics/#relabeling + properties: + action: + description: Action to perform based on regex matching. Default + is 'replace' + type: string + if: + description: 'If represents metricsQL match expression (or list + of expressions): ''{__name__=~"foo_.*"}''' + x-kubernetes-preserve-unknown-fields: true + labels: + additionalProperties: + type: string + description: 'Labels is used together with Match for `action: + graphite`' + type: object + match: + description: 'Match is used together with Labels for `action: + graphite`' + type: string + modulus: + description: Modulus to take of the hash of the source label + values. + format: int64 + type: integer + regex: + description: |- + Regular expression against which the extracted value is matched. Default is '(.*)' + victoriaMetrics supports multiline regex joined with | + https://docs.victoriametrics.com/victoriametrics/vmagent/#relabeling-enhancements + x-kubernetes-preserve-unknown-fields: true + replacement: + description: |- + Replacement value against which a regex replace is performed if the + regular expression matches. Regex capture groups are available. Default is '$1' + type: string + separator: + description: Separator placed between concatenated source label + values. default is ';'. + type: string + source_labels: + description: |- + UnderScoreSourceLabels - additional form of source labels source_labels + for compatibility with original relabel config. + if set both sourceLabels and source_labels, sourceLabels has priority. + for details https://github.com/VictoriaMetrics/operator/issues/131 + items: + type: string + type: array + sourceLabels: + description: |- + The source labels select values from existing labels. Their content is concatenated + using the configured separator and matched against the configured regular expression + for the replace, keep, and drop actions. + items: + type: string + type: array + target_label: + description: |- + UnderScoreTargetLabel - additional form of target label - target_label + for compatibility with original relabel config. + if set both targetLabel and target_label, targetLabel has priority. + for details https://github.com/VictoriaMetrics/operator/issues/131 + type: string + targetLabel: + description: |- + Label to which the resulting value is written in a replace action. + It is mandatory for replace actions. Regex capture groups are available. + type: string + type: object + type: array + host_aliases: + description: |- + HostAliasesUnderScore provides mapping for ip and hostname, + that would be propagated to pod, + cannot be used with HostNetwork. + Has Priority over hostAliases field + items: + description: |- + HostAlias holds the mapping between IP and hostnames that will be injected as an entry in the + pod's hosts file. + properties: + hostnames: + description: Hostnames for the above IP address. + items: + type: string + type: array + x-kubernetes-list-type: atomic + ip: + description: IP address of the host file entry. + type: string + required: + - ip + type: object + type: array + hostAliases: + description: |- + HostAliases provides mapping for ip and hostname, + that would be propagated to pod, + cannot be used with HostNetwork. + items: + description: |- + HostAlias holds the mapping between IP and hostnames that will be injected as an entry in the + pod's hosts file. + properties: + hostnames: + description: Hostnames for the above IP address. + items: + type: string + type: array + x-kubernetes-list-type: atomic + ip: description: IP address of the host file entry. type: string required: @@ -39536,6 +40127,12 @@ spec: description: HostNetwork controls whether the pod may use the node network namespace type: boolean + ignoreNamespaceSelectors: + description: |- + IgnoreNamespaceSelectors if set to true will ignore NamespaceSelector settings from + scrape objects, and they will only discover endpoints + within their current namespace. Defaults to false. + type: boolean image: description: |- Image - docker image settings @@ -39574,6 +40171,12 @@ spec: type: object x-kubernetes-map-type: atomic type: array + ingestOnlyMode: + description: |- + IngestOnlyMode switches vmagent or vmsingle into unmanaged mode + it disables any config generation for scraping + Currently it prevents vmagent from managing tls and auth options for remote write + type: boolean initContainers: description: |- InitContainers allows adding initContainers to the pod definition. @@ -39587,6 +40190,96 @@ spec: type: object x-kubernetes-preserve-unknown-fields: true type: array + inlineRelabelConfig: + description: InlineRelabelConfig - defines GlobalRelabelConfig for + vmagent, can be defined directly at CRD. + items: + description: |- + RelabelConfig allows dynamic rewriting of the label set + More info: https://docs.victoriametrics.com/victoriametrics/#relabeling + properties: + action: + description: Action to perform based on regex matching. Default + is 'replace' + type: string + if: + description: 'If represents metricsQL match expression (or list + of expressions): ''{__name__=~"foo_.*"}''' + x-kubernetes-preserve-unknown-fields: true + labels: + additionalProperties: + type: string + description: 'Labels is used together with Match for `action: + graphite`' + type: object + match: + description: 'Match is used together with Labels for `action: + graphite`' + type: string + modulus: + description: Modulus to take of the hash of the source label + values. + format: int64 + type: integer + regex: + description: |- + Regular expression against which the extracted value is matched. Default is '(.*)' + victoriaMetrics supports multiline regex joined with | + https://docs.victoriametrics.com/victoriametrics/vmagent/#relabeling-enhancements + x-kubernetes-preserve-unknown-fields: true + replacement: + description: |- + Replacement value against which a regex replace is performed if the + regular expression matches. Regex capture groups are available. Default is '$1' + type: string + separator: + description: Separator placed between concatenated source label + values. default is ';'. + type: string + source_labels: + description: |- + UnderScoreSourceLabels - additional form of source labels source_labels + for compatibility with original relabel config. + if set both sourceLabels and source_labels, sourceLabels has priority. + for details https://github.com/VictoriaMetrics/operator/issues/131 + items: + type: string + type: array + sourceLabels: + description: |- + The source labels select values from existing labels. Their content is concatenated + using the configured separator and matched against the configured regular expression + for the replace, keep, and drop actions. + items: + type: string + type: array + target_label: + description: |- + UnderScoreTargetLabel - additional form of target label - target_label + for compatibility with original relabel config. + if set both targetLabel and target_label, targetLabel has priority. + for details https://github.com/VictoriaMetrics/operator/issues/131 + type: string + targetLabel: + description: |- + Label to which the resulting value is written in a replace action. + It is mandatory for replace actions. Regex capture groups are available. + type: string + type: object + type: array + inlineScrapeConfig: + description: |- + 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 or VMSingle. It is advised to review VMAgent or VMSingle release + notes to ensure that no incompatible scrape configs are going to break + VMAgent or VMSingle after the upgrade. + it should be defined as single yaml file. + inlineScrapeConfig: | + - job_name: "prometheus" + static_configs: + - targets: ["localhost:9090"] + type: string insertPorts: description: InsertPorts - additional listen ports for data ingestion. properties: @@ -39690,6 +40383,11 @@ spec: More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels type: object type: object + maxScrapeInterval: + description: |- + MaxScrapeInterval allows limiting maximum scrape interval for VMServiceScrape, VMPodScrape and other scrapes + If interval is higher than defined limit, `maxScrapeInterval` will be used. + type: string minReadySeconds: description: |- MinReadySeconds defines a minimum number of seconds to wait before starting update next pod @@ -39697,12 +40395,206 @@ spec: Has no effect for VLogs and VMSingle format: int32 type: integer + minScrapeInterval: + description: |- + MinScrapeInterval allows limiting minimal scrape interval for VMServiceScrape, VMPodScrape and other scrapes + If interval is lower than defined limit, `minScrapeInterval` will be used. + type: string + nodeScrapeNamespaceSelector: + description: |- + NodeScrapeNamespaceSelector defines Namespaces to be selected for VMNodeScrape discovery. + Works in combination with Selector. + NamespaceSelector nil - only objects at VMAgent or VMSingle namespace. + Selector nil - only objects at NamespaceSelector namespaces. + If both nil - behaviour controlled by selectAllByDefault + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + nodeScrapeRelabelTemplate: + description: |- + NodeScrapeRelabelTemplate defines relabel config, that will be added to each VMNodeScrape. + it's useful for adding specific labels to all targets + items: + description: |- + RelabelConfig allows dynamic rewriting of the label set + More info: https://docs.victoriametrics.com/victoriametrics/#relabeling + properties: + action: + description: Action to perform based on regex matching. Default + is 'replace' + type: string + if: + description: 'If represents metricsQL match expression (or list + of expressions): ''{__name__=~"foo_.*"}''' + x-kubernetes-preserve-unknown-fields: true + labels: + additionalProperties: + type: string + description: 'Labels is used together with Match for `action: + graphite`' + type: object + match: + description: 'Match is used together with Labels for `action: + graphite`' + type: string + modulus: + description: Modulus to take of the hash of the source label + values. + format: int64 + type: integer + regex: + description: |- + Regular expression against which the extracted value is matched. Default is '(.*)' + victoriaMetrics supports multiline regex joined with | + https://docs.victoriametrics.com/victoriametrics/vmagent/#relabeling-enhancements + x-kubernetes-preserve-unknown-fields: true + replacement: + description: |- + Replacement value against which a regex replace is performed if the + regular expression matches. Regex capture groups are available. Default is '$1' + type: string + separator: + description: Separator placed between concatenated source label + values. default is ';'. + type: string + source_labels: + description: |- + UnderScoreSourceLabels - additional form of source labels source_labels + for compatibility with original relabel config. + if set both sourceLabels and source_labels, sourceLabels has priority. + for details https://github.com/VictoriaMetrics/operator/issues/131 + items: + type: string + type: array + sourceLabels: + description: |- + The source labels select values from existing labels. Their content is concatenated + using the configured separator and matched against the configured regular expression + for the replace, keep, and drop actions. + items: + type: string + type: array + target_label: + description: |- + UnderScoreTargetLabel - additional form of target label - target_label + for compatibility with original relabel config. + if set both targetLabel and target_label, targetLabel has priority. + for details https://github.com/VictoriaMetrics/operator/issues/131 + type: string + targetLabel: + description: |- + Label to which the resulting value is written in a replace action. + It is mandatory for replace actions. Regex capture groups are available. + type: string + type: object + type: array + nodeScrapeSelector: + description: |- + NodeScrapeSelector defines VMNodeScrape to be selected for scraping. + Works in combination with NamespaceSelector. + NamespaceSelector nil - only objects at VMAgent or VMSingle namespace. + Selector nil - only objects at NamespaceSelector namespaces. + If both nil - behaviour controlled by selectAllByDefault + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic nodeSelector: additionalProperties: type: string description: NodeSelector Define which Nodes the Pods are scheduled on. type: object + overrideHonorLabels: + description: |- + OverrideHonorLabels if set to true overrides all user configured honor_labels. + If HonorLabels is set in scrape objects to true, this overrides honor_labels to false. + type: boolean + overrideHonorTimestamps: + description: OverrideHonorTimestamps allows to globally enforce honoring + timestamps in all scrape configs. + type: boolean paused: description: |- Paused If set to true all actions on the underlying managed objects are not @@ -39740,123 +40632,1287 @@ spec: More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names type: string type: object - port: - description: Port listen address - type: string - priorityClassName: - description: PriorityClassName class assigned to the Pods - type: string - readinessGates: - description: ReadinessGates defines pod readiness gates + podScrapeNamespaceSelector: + description: |- + PodScrapeNamespaceSelector defines Namespaces to be selected for VMPodScrape discovery. + Works in combination with Selector. + NamespaceSelector nil - only objects at VMAgent or VMSingle namespace. + Selector nil - only objects at NamespaceSelector namespaces. + If both nil - behaviour controlled by selectAllByDefault + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + podScrapeRelabelTemplate: + description: |- + PodScrapeRelabelTemplate defines relabel config, that will be added to each VMPodScrape. + it's useful for adding specific labels to all targets items: - description: PodReadinessGate contains the reference to a pod condition + description: |- + RelabelConfig allows dynamic rewriting of the label set + More info: https://docs.victoriametrics.com/victoriametrics/#relabeling properties: - conditionType: - description: ConditionType refers to a condition in the pod's - condition list with matching type. + action: + description: Action to perform based on regex matching. Default + is 'replace' + type: string + if: + description: 'If represents metricsQL match expression (or list + of expressions): ''{__name__=~"foo_.*"}''' + x-kubernetes-preserve-unknown-fields: true + labels: + additionalProperties: + type: string + description: 'Labels is used together with Match for `action: + graphite`' + type: object + match: + description: 'Match is used together with Labels for `action: + graphite`' + type: string + modulus: + description: Modulus to take of the hash of the source label + values. + format: int64 + type: integer + regex: + description: |- + Regular expression against which the extracted value is matched. Default is '(.*)' + victoriaMetrics supports multiline regex joined with | + https://docs.victoriametrics.com/victoriametrics/vmagent/#relabeling-enhancements + x-kubernetes-preserve-unknown-fields: true + replacement: + description: |- + Replacement value against which a regex replace is performed if the + regular expression matches. Regex capture groups are available. Default is '$1' + type: string + separator: + description: Separator placed between concatenated source label + values. default is ';'. + type: string + source_labels: + description: |- + UnderScoreSourceLabels - additional form of source labels source_labels + for compatibility with original relabel config. + if set both sourceLabels and source_labels, sourceLabels has priority. + for details https://github.com/VictoriaMetrics/operator/issues/131 + items: + type: string + type: array + sourceLabels: + description: |- + The source labels select values from existing labels. Their content is concatenated + using the configured separator and matched against the configured regular expression + for the replace, keep, and drop actions. + items: + type: string + type: array + target_label: + description: |- + UnderScoreTargetLabel - additional form of target label - target_label + for compatibility with original relabel config. + if set both targetLabel and target_label, targetLabel has priority. + for details https://github.com/VictoriaMetrics/operator/issues/131 + type: string + targetLabel: + description: |- + Label to which the resulting value is written in a replace action. + It is mandatory for replace actions. Regex capture groups are available. + type: string + type: object + type: array + podScrapeSelector: + description: |- + PodScrapeSelector defines PodScrapes to be selected for target discovery. + Works in combination with NamespaceSelector. + NamespaceSelector nil - only objects at VMAgent or VMSingle namespace. + Selector nil - only objects at NamespaceSelector namespaces. + If both nil - behaviour controlled by selectAllByDefault + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + port: + description: Port listen address + type: string + priorityClassName: + description: PriorityClassName class assigned to the Pods + type: string + probeNamespaceSelector: + description: |- + ProbeNamespaceSelector defines Namespaces to be selected for VMProbe discovery. + Works in combination with Selector. + NamespaceSelector nil - only objects at VMAgent or VMSingle namespace. + Selector nil - only objects at NamespaceSelector namespaces. + If both nil - behaviour controlled by selectAllByDefault + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + probeScrapeRelabelTemplate: + description: |- + ProbeScrapeRelabelTemplate defines relabel config, that will be added to each VMProbeScrape. + it's useful for adding specific labels to all targets + items: + description: |- + RelabelConfig allows dynamic rewriting of the label set + More info: https://docs.victoriametrics.com/victoriametrics/#relabeling + properties: + action: + description: Action to perform based on regex matching. Default + is 'replace' + type: string + if: + description: 'If represents metricsQL match expression (or list + of expressions): ''{__name__=~"foo_.*"}''' + x-kubernetes-preserve-unknown-fields: true + labels: + additionalProperties: + type: string + description: 'Labels is used together with Match for `action: + graphite`' + type: object + match: + description: 'Match is used together with Labels for `action: + graphite`' + type: string + modulus: + description: Modulus to take of the hash of the source label + values. + format: int64 + type: integer + regex: + description: |- + Regular expression against which the extracted value is matched. Default is '(.*)' + victoriaMetrics supports multiline regex joined with | + https://docs.victoriametrics.com/victoriametrics/vmagent/#relabeling-enhancements + x-kubernetes-preserve-unknown-fields: true + replacement: + description: |- + Replacement value against which a regex replace is performed if the + regular expression matches. Regex capture groups are available. Default is '$1' + type: string + separator: + description: Separator placed between concatenated source label + values. default is ';'. + type: string + source_labels: + description: |- + UnderScoreSourceLabels - additional form of source labels source_labels + for compatibility with original relabel config. + if set both sourceLabels and source_labels, sourceLabels has priority. + for details https://github.com/VictoriaMetrics/operator/issues/131 + items: + type: string + type: array + sourceLabels: + description: |- + The source labels select values from existing labels. Their content is concatenated + using the configured separator and matched against the configured regular expression + for the replace, keep, and drop actions. + items: + type: string + type: array + target_label: + description: |- + UnderScoreTargetLabel - additional form of target label - target_label + for compatibility with original relabel config. + if set both targetLabel and target_label, targetLabel has priority. + for details https://github.com/VictoriaMetrics/operator/issues/131 + type: string + targetLabel: + description: |- + Label to which the resulting value is written in a replace action. + It is mandatory for replace actions. Regex capture groups are available. + type: string + type: object + type: array + probeSelector: + description: |- + ProbeSelector defines VMProbe to be selected for target probing. + Works in combination with NamespaceSelector. + NamespaceSelector nil - only objects at VMAgent or VMSingle namespace. + Selector nil - only objects at NamespaceSelector namespaces. + If both nil - behaviour controlled by selectAllByDefault + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + readinessGates: + description: ReadinessGates defines pod readiness gates + items: + description: PodReadinessGate contains the reference to a pod condition + properties: + conditionType: + description: ConditionType refers to a condition in the pod's + condition list with matching type. + type: string + required: + - conditionType + type: object + type: array + readinessProbe: + description: ReadinessProbe that will be added CRD pod + type: object + x-kubernetes-preserve-unknown-fields: true + relabelConfig: + description: |- + RelabelConfig ConfigMap with global relabel config -remoteWrite.relabelConfig + This relabeling is applied to all the collected metrics before sending them to remote storage. + properties: + key: + description: The key to select. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the ConfigMap or its key must be + defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + removePvcAfterDelete: + description: |- + RemovePvcAfterDelete - if true, controller adds ownership to pvc + and after VMSingle object deletion - pvc will be garbage collected + by controller manager + type: boolean + replicaCount: + description: ReplicaCount is the expected size of the Application. + format: int32 + type: integer + resources: + description: |- + 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 + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This field depends on the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + retentionPeriod: + description: |- + RetentionPeriod defines how long to retain stored metrics, specified as a duration (e.g., "1d", "1w", "1m"). + Data with timestamps outside the RetentionPeriod is automatically deleted. The minimum allowed value is 1d, or 24h. + The default value is 1 (one month). + See [retention](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#retention) docs for details. + pattern: ^[0-9]+(h|d|w|y)?$ + type: string + revisionHistoryLimitCount: + description: |- + 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. + format: int32 + type: integer + runtimeClassName: + description: |- + RuntimeClassName - defines runtime class for kubernetes pod. + https://kubernetes.io/docs/concepts/containers/runtime-class/ + type: string + sampleLimit: + description: SampleLimit defines global per target limit of scraped + samples + type: integer + schedulerName: + description: SchedulerName - defines kubernetes scheduler name + type: string + scrapeClasses: + description: |- + ScrapeClasses defines the list of scrape classes to expose to scraping objects such as + PodScrapes, ServiceScrapes, Probes and ScrapeConfigs. + items: + properties: + attachMetadata: + description: |- + AttachMetadata defines additional metadata to the discovered targets. + When the scrape object defines its own configuration, it takes + precedence over the scrape class configuration. + properties: + namespace: + description: |- + Namespace instructs vmagent to add namespace specific metadata from service discovery + Valid for roles: pod, service, endpoints, endpointslice, ingress. + type: boolean + node: + description: |- + Node instructs vmagent to add node specific metadata from service discovery + Valid for roles: pod, endpoints, endpointslice. + type: boolean + type: object + authorization: + description: Authorization with http header Authorization + properties: + credentials: + description: Reference to the secret with value for authorization + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + credentialsFile: + description: File with value for authorization + type: string + type: + description: Type of authorization, default to bearer + type: string + type: object + basicAuth: + description: BasicAuth allow an endpoint to authenticate over + basic authentication + properties: + password: + description: |- + Password defines reference for secret with password value + The secret needs to be in the same namespace as scrape object + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + password_file: + description: |- + PasswordFile defines path to password file at disk + must be pre-mounted + type: string + username: + description: |- + Username defines reference for secret with username value + The secret needs to be in the same namespace as scrape object + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + bearerTokenFile: + description: File to read bearer token for scraping targets. + type: string + bearerTokenSecret: + description: |- + Secret to mount to read bearer token for scraping targets. The secret + needs to be in the same namespace as the scrape object and accessible by + the victoria-metrics operator. + nullable: true + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + default: + description: |- + default defines that the scrape applies to all scrape objects that + don't configure an explicit scrape class name. + + Only one scrape class can be set as the default. + type: boolean + metricRelabelConfigs: + description: MetricRelabelConfigs to apply to samples after + scrapping. + items: + description: |- + RelabelConfig allows dynamic rewriting of the label set + More info: https://docs.victoriametrics.com/victoriametrics/#relabeling + properties: + action: + description: Action to perform based on regex matching. + Default is 'replace' + type: string + if: + description: 'If represents metricsQL match expression + (or list of expressions): ''{__name__=~"foo_.*"}''' + x-kubernetes-preserve-unknown-fields: true + labels: + additionalProperties: + type: string + description: 'Labels is used together with Match for `action: + graphite`' + type: object + match: + description: 'Match is used together with Labels for `action: + graphite`' + type: string + modulus: + description: Modulus to take of the hash of the source + label values. + format: int64 + type: integer + regex: + description: |- + Regular expression against which the extracted value is matched. Default is '(.*)' + victoriaMetrics supports multiline regex joined with | + https://docs.victoriametrics.com/victoriametrics/vmagent/#relabeling-enhancements + x-kubernetes-preserve-unknown-fields: true + replacement: + description: |- + Replacement value against which a regex replace is performed if the + regular expression matches. Regex capture groups are available. Default is '$1' + type: string + separator: + description: Separator placed between concatenated source + label values. default is ';'. + type: string + source_labels: + description: |- + UnderScoreSourceLabels - additional form of source labels source_labels + for compatibility with original relabel config. + if set both sourceLabels and source_labels, sourceLabels has priority. + for details https://github.com/VictoriaMetrics/operator/issues/131 + items: + type: string + type: array + sourceLabels: + description: |- + The source labels select values from existing labels. Their content is concatenated + using the configured separator and matched against the configured regular expression + for the replace, keep, and drop actions. + items: + type: string + type: array + target_label: + description: |- + UnderScoreTargetLabel - additional form of target label - target_label + for compatibility with original relabel config. + if set both targetLabel and target_label, targetLabel has priority. + for details https://github.com/VictoriaMetrics/operator/issues/131 + type: string + targetLabel: + description: |- + Label to which the resulting value is written in a replace action. + It is mandatory for replace actions. Regex capture groups are available. + type: string + type: object + type: array + name: + description: name of the scrape class. + minLength: 1 + type: string + oauth2: + description: OAuth2 defines auth configuration + properties: + client_id: + description: The secret or configmap containing the OAuth2 + client id + properties: + configMap: + description: ConfigMap containing data to use for the + targets. + properties: + key: + description: The key to select. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the ConfigMap or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + description: Secret containing data to use for the targets. + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + client_secret: + description: The secret containing the OAuth2 client secret + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + client_secret_file: + description: ClientSecretFile defines path for client secret + file. + type: string + endpoint_params: + additionalProperties: + type: string + description: Parameters to append to the token URL + type: object + proxy_url: + description: |- + The proxy URL for token_url connection + ( available from v0.55.0). + Is only supported by Scrape objects family + type: string + scopes: + description: OAuth2 scopes used for the token request + items: + type: string + type: array + tls_config: + description: |- + TLSConfig for token_url connection + ( available from v0.55.0). + Is only supported by Scrape objects family + x-kubernetes-preserve-unknown-fields: true + token_url: + description: The URL to fetch the token from + minLength: 1 + type: string + required: + - client_id + - token_url + type: object + relabelConfigs: + description: RelabelConfigs to apply to samples during service + discovery. + items: + description: |- + RelabelConfig allows dynamic rewriting of the label set + More info: https://docs.victoriametrics.com/victoriametrics/#relabeling + properties: + action: + description: Action to perform based on regex matching. + Default is 'replace' + type: string + if: + description: 'If represents metricsQL match expression + (or list of expressions): ''{__name__=~"foo_.*"}''' + x-kubernetes-preserve-unknown-fields: true + labels: + additionalProperties: + type: string + description: 'Labels is used together with Match for `action: + graphite`' + type: object + match: + description: 'Match is used together with Labels for `action: + graphite`' + type: string + modulus: + description: Modulus to take of the hash of the source + label values. + format: int64 + type: integer + regex: + description: |- + Regular expression against which the extracted value is matched. Default is '(.*)' + victoriaMetrics supports multiline regex joined with | + https://docs.victoriametrics.com/victoriametrics/vmagent/#relabeling-enhancements + x-kubernetes-preserve-unknown-fields: true + replacement: + description: |- + Replacement value against which a regex replace is performed if the + regular expression matches. Regex capture groups are available. Default is '$1' + type: string + separator: + description: Separator placed between concatenated source + label values. default is ';'. + type: string + source_labels: + description: |- + UnderScoreSourceLabels - additional form of source labels source_labels + for compatibility with original relabel config. + if set both sourceLabels and source_labels, sourceLabels has priority. + for details https://github.com/VictoriaMetrics/operator/issues/131 + items: + type: string + type: array + sourceLabels: + description: |- + The source labels select values from existing labels. Their content is concatenated + using the configured separator and matched against the configured regular expression + for the replace, keep, and drop actions. + items: + type: string + type: array + target_label: + description: |- + UnderScoreTargetLabel - additional form of target label - target_label + for compatibility with original relabel config. + if set both targetLabel and target_label, targetLabel has priority. + for details https://github.com/VictoriaMetrics/operator/issues/131 + type: string + targetLabel: + description: |- + Label to which the resulting value is written in a replace action. + It is mandatory for replace actions. Regex capture groups are available. + type: string + type: object + type: array + tlsConfig: + description: TLSConfig configuration to use when scraping the + endpoint + properties: + ca: + description: Struct containing the CA cert to use for the + targets. + properties: + configMap: + description: ConfigMap containing data to use for the + targets. + properties: + key: + description: The key to select. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the ConfigMap or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + description: Secret containing data to use for the targets. + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + caFile: + description: Path to the CA cert in the container to use + for the targets. + type: string + cert: + description: Struct containing the client cert file for + the targets. + properties: + configMap: + description: ConfigMap containing data to use for the + targets. + properties: + key: + description: The key to select. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the ConfigMap or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + secret: + description: Secret containing data to use for the targets. + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + certFile: + description: Path to the client cert file in the container + for the targets. + type: string + insecureSkipVerify: + description: Disable target certificate validation. + type: boolean + keyFile: + description: Path to the client key file in the container + for the targets. + type: string + keySecret: + description: Secret containing the client key file for the + targets. + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + serverName: + description: Used to verify the hostname for the targets. + type: string + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + scrapeConfigNamespaceSelector: + description: |- + ScrapeConfigNamespaceSelector defines Namespaces to be selected for VMScrapeConfig discovery. + Works in combination with Selector. + NamespaceSelector nil - only objects at VMAgent or VMSingle namespace. + Selector nil - only objects at NamespaceSelector namespaces. + If both nil - behaviour controlled by selectAllByDefault + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + scrapeConfigRelabelTemplate: + description: |- + ScrapeConfigRelabelTemplate defines relabel config, that will be added to each VMScrapeConfig. + it's useful for adding specific labels to all targets + items: + description: |- + RelabelConfig allows dynamic rewriting of the label set + More info: https://docs.victoriametrics.com/victoriametrics/#relabeling + properties: + action: + description: Action to perform based on regex matching. Default + is 'replace' + type: string + if: + description: 'If represents metricsQL match expression (or list + of expressions): ''{__name__=~"foo_.*"}''' + x-kubernetes-preserve-unknown-fields: true + labels: + additionalProperties: + type: string + description: 'Labels is used together with Match for `action: + graphite`' + type: object + match: + description: 'Match is used together with Labels for `action: + graphite`' + type: string + modulus: + description: Modulus to take of the hash of the source label + values. + format: int64 + type: integer + regex: + description: |- + Regular expression against which the extracted value is matched. Default is '(.*)' + victoriaMetrics supports multiline regex joined with | + https://docs.victoriametrics.com/victoriametrics/vmagent/#relabeling-enhancements + x-kubernetes-preserve-unknown-fields: true + replacement: + description: |- + Replacement value against which a regex replace is performed if the + regular expression matches. Regex capture groups are available. Default is '$1' + type: string + separator: + description: Separator placed between concatenated source label + values. default is ';'. + type: string + source_labels: + description: |- + UnderScoreSourceLabels - additional form of source labels source_labels + for compatibility with original relabel config. + if set both sourceLabels and source_labels, sourceLabels has priority. + for details https://github.com/VictoriaMetrics/operator/issues/131 + items: + type: string + type: array + sourceLabels: + description: |- + The source labels select values from existing labels. Their content is concatenated + using the configured separator and matched against the configured regular expression + for the replace, keep, and drop actions. + items: + type: string + type: array + target_label: + description: |- + UnderScoreTargetLabel - additional form of target label - target_label + for compatibility with original relabel config. + if set both targetLabel and target_label, targetLabel has priority. + for details https://github.com/VictoriaMetrics/operator/issues/131 + type: string + targetLabel: + description: |- + Label to which the resulting value is written in a replace action. + It is mandatory for replace actions. Regex capture groups are available. type: string - required: - - conditionType type: object type: array - readinessProbe: - description: ReadinessProbe that will be added CRD pod - type: object - x-kubernetes-preserve-unknown-fields: true - removePvcAfterDelete: - description: |- - RemovePvcAfterDelete - if true, controller adds ownership to pvc - and after VMSingle object deletion - pvc will be garbage collected - by controller manager - type: boolean - replicaCount: - description: ReplicaCount is the expected size of the Application. - format: int32 - type: integer - resources: + scrapeConfigSelector: description: |- - 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 + ScrapeConfigSelector defines VMScrapeConfig to be selected for target discovery. + Works in combination with NamespaceSelector. properties: - claims: - description: |- - Claims lists the names of resources, defined in spec.resourceClaims, - that are used by this container. - - This field depends on the - DynamicResourceAllocation feature gate. - - This field is immutable. It can only be set for containers. + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. items: - description: ResourceClaim references one entry in PodSpec.ResourceClaims. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: - name: - description: |- - Name must match the name of one entry in pod.spec.resourceClaims of - the Pod where this field is used. It makes that resource available - inside a container. + key: + description: key is the label key that the selector applies + to. type: string - request: + operator: description: |- - Request is the name chosen for a request in the referenced claim. - If empty, everything from the claim is made available, otherwise - only the result of this request. + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic required: - - name + - key + - operator type: object type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: |- - Limits describes the maximum amount of compute resources allowed. - More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ - type: object - requests: + x-kubernetes-list-type: atomic + matchLabels: additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true + type: string description: |- - Requests describes the minimum amount of compute resources required. - If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, - otherwise to an implementation-defined value. Requests cannot exceed Limits. - More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object - retentionPeriod: - description: |- - RetentionPeriod defines how long to retain stored metrics, specified as a duration (e.g., "1d", "1w", "1m"). - Data with timestamps outside the RetentionPeriod is automatically deleted. The minimum allowed value is 1d, or 24h. - The default value is 1 (one month). - See [retention](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#retention) docs for details. - pattern: ^[0-9]+(h|d|w|y)?$ - type: string - revisionHistoryLimitCount: - description: |- - 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. - format: int32 - type: integer - runtimeClassName: - description: |- - RuntimeClassName - defines runtime class for kubernetes pod. - https://kubernetes.io/docs/concepts/containers/runtime-class/ + x-kubernetes-map-type: atomic + scrapeInterval: + description: ScrapeInterval defines how often scrape targets by default + pattern: '[0-9]+(ms|s|m|h)' type: string - schedulerName: - description: SchedulerName - defines kubernetes scheduler name + scrapeTimeout: + description: ScrapeTimeout defines global timeout for targets scrape + pattern: '[0-9]+(ms|s|m|h)' type: string secrets: description: |- @@ -39872,10 +41928,197 @@ spec: This defaults to the default PodSecurityContext. type: object x-kubernetes-preserve-unknown-fields: true + selectAllByDefault: + description: |- + SelectAllByDefault changes default behavior for empty CRD selectors, such ServiceScrapeSelector. + with selectAllByDefault: true and empty serviceScrapeSelector and ServiceScrapeNamespaceSelector + Operator selects all exist serviceScrapes + with selectAllByDefault: false - selects nothing + type: boolean serviceAccountName: description: ServiceAccountName is the name of the ServiceAccount to use to run the pods type: string + serviceScrapeNamespaceSelector: + description: |- + ServiceScrapeNamespaceSelector Namespaces to be selected for VMServiceScrape discovery. + Works in combination with Selector. + NamespaceSelector nil - only objects at VMAgent or VMSingle namespace. + Selector nil - only objects at NamespaceSelector namespaces. + If both nil - behaviour controlled by selectAllByDefault + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + serviceScrapeRelabelTemplate: + description: |- + ServiceScrapeRelabelTemplate defines relabel config, that will be added to each VMServiceScrape. + it's useful for adding specific labels to all targets + items: + description: |- + RelabelConfig allows dynamic rewriting of the label set + More info: https://docs.victoriametrics.com/victoriametrics/#relabeling + properties: + action: + description: Action to perform based on regex matching. Default + is 'replace' + type: string + if: + description: 'If represents metricsQL match expression (or list + of expressions): ''{__name__=~"foo_.*"}''' + x-kubernetes-preserve-unknown-fields: true + labels: + additionalProperties: + type: string + description: 'Labels is used together with Match for `action: + graphite`' + type: object + match: + description: 'Match is used together with Labels for `action: + graphite`' + type: string + modulus: + description: Modulus to take of the hash of the source label + values. + format: int64 + type: integer + regex: + description: |- + Regular expression against which the extracted value is matched. Default is '(.*)' + victoriaMetrics supports multiline regex joined with | + https://docs.victoriametrics.com/victoriametrics/vmagent/#relabeling-enhancements + x-kubernetes-preserve-unknown-fields: true + replacement: + description: |- + Replacement value against which a regex replace is performed if the + regular expression matches. Regex capture groups are available. Default is '$1' + type: string + separator: + description: Separator placed between concatenated source label + values. default is ';'. + type: string + source_labels: + description: |- + UnderScoreSourceLabels - additional form of source labels source_labels + for compatibility with original relabel config. + if set both sourceLabels and source_labels, sourceLabels has priority. + for details https://github.com/VictoriaMetrics/operator/issues/131 + items: + type: string + type: array + sourceLabels: + description: |- + The source labels select values from existing labels. Their content is concatenated + using the configured separator and matched against the configured regular expression + for the replace, keep, and drop actions. + items: + type: string + type: array + target_label: + description: |- + UnderScoreTargetLabel - additional form of target label - target_label + for compatibility with original relabel config. + if set both targetLabel and target_label, targetLabel has priority. + for details https://github.com/VictoriaMetrics/operator/issues/131 + type: string + targetLabel: + description: |- + Label to which the resulting value is written in a replace action. + It is mandatory for replace actions. Regex capture groups are available. + type: string + type: object + type: array + serviceScrapeSelector: + description: |- + ServiceScrapeSelector defines ServiceScrapes to be selected for target discovery. + Works in combination with NamespaceSelector. + NamespaceSelector nil - only objects at VMAgent or VMSingle namespace. + Selector nil - only objects at NamespaceSelector namespaces. + If both nil - behaviour controlled by selectAllByDefault + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic serviceScrapeSpec: description: ServiceScrapeSpec that will be added to vmsingle VMServiceScrape spec @@ -39936,6 +42179,186 @@ spec: description: StartupProbe that will be added to CRD pod type: object x-kubernetes-preserve-unknown-fields: true + staticScrapeNamespaceSelector: + description: |- + StaticScrapeNamespaceSelector defines Namespaces to be selected for VMStaticScrape discovery. + Works in combination with NamespaceSelector. + NamespaceSelector nil - only objects at VMAgent or VMSingle namespace. + Selector nil - only objects at NamespaceSelector namespaces. + If both nil - behaviour controlled by selectAllByDefault + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + staticScrapeRelabelTemplate: + description: |- + StaticScrapeRelabelTemplate defines relabel config, that will be added to each VMStaticScrape. + it's useful for adding specific labels to all targets + items: + description: |- + RelabelConfig allows dynamic rewriting of the label set + More info: https://docs.victoriametrics.com/victoriametrics/#relabeling + properties: + action: + description: Action to perform based on regex matching. Default + is 'replace' + type: string + if: + description: 'If represents metricsQL match expression (or list + of expressions): ''{__name__=~"foo_.*"}''' + x-kubernetes-preserve-unknown-fields: true + labels: + additionalProperties: + type: string + description: 'Labels is used together with Match for `action: + graphite`' + type: object + match: + description: 'Match is used together with Labels for `action: + graphite`' + type: string + modulus: + description: Modulus to take of the hash of the source label + values. + format: int64 + type: integer + regex: + description: |- + Regular expression against which the extracted value is matched. Default is '(.*)' + victoriaMetrics supports multiline regex joined with | + https://docs.victoriametrics.com/victoriametrics/vmagent/#relabeling-enhancements + x-kubernetes-preserve-unknown-fields: true + replacement: + description: |- + Replacement value against which a regex replace is performed if the + regular expression matches. Regex capture groups are available. Default is '$1' + type: string + separator: + description: Separator placed between concatenated source label + values. default is ';'. + type: string + source_labels: + description: |- + UnderScoreSourceLabels - additional form of source labels source_labels + for compatibility with original relabel config. + if set both sourceLabels and source_labels, sourceLabels has priority. + for details https://github.com/VictoriaMetrics/operator/issues/131 + items: + type: string + type: array + sourceLabels: + description: |- + The source labels select values from existing labels. Their content is concatenated + using the configured separator and matched against the configured regular expression + for the replace, keep, and drop actions. + items: + type: string + type: array + target_label: + description: |- + UnderScoreTargetLabel - additional form of target label - target_label + for compatibility with original relabel config. + if set both targetLabel and target_label, targetLabel has priority. + for details https://github.com/VictoriaMetrics/operator/issues/131 + type: string + targetLabel: + description: |- + Label to which the resulting value is written in a replace action. + It is mandatory for replace actions. Regex capture groups are available. + type: string + type: object + type: array + staticScrapeSelector: + description: |- + StaticScrapeSelector defines VMStaticScrape to be selected for target discovery. + Works in combination with NamespaceSelector. + If both nil - match everything. + NamespaceSelector nil - only objects at VMAgent or VMSingle namespace. + Selector nil - only objects at NamespaceSelector namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic storage: description: |- Storage is the definition of how storage will be used by the VMSingle @@ -40574,6 +42997,20 @@ spec: uses non-root user out of the box drops not needed security permissions type: boolean + useVMConfigReloader: + description: |- + 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 + type: boolean + vmAgentExternalLabelName: + description: |- + VMAgentExternalLabelName Name of vmAgent external label used to denote vmAgent instance + name. Defaults to the value of `prometheus`. External label will + _not_ be added when value is set to empty string (`""`). + Deprecated: use externalLabelName instead. will be removed in v0.69.0 + type: string vmBackup: description: VMBackup configuration for backup properties: diff --git a/config/examples/vmagent-full.yaml b/config/examples/vmagent-full.yaml index 9cfde39ec..ea4d061ea 100644 --- a/config/examples/vmagent-full.yaml +++ b/config/examples/vmagent-full.yaml @@ -83,7 +83,7 @@ spec: repository: victoriametrics/vmagent tag: v1.46.0 pullPolicy: IfNotPresent - vmAgentExternalLabelName: vmagent + externalLabelName: vmagent scrapeInterval: 30s externalLabels: key: value diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index f55f73cd1..9b602a8e0 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -29,6 +29,7 @@ aliases: * FEATURE: [vlagent](https://docs.victoriametrics.com/operator/resources/vlagent): support logs collection. See [#1501](https://github.com/VictoriaMetrics/operator/issues/1501). * FEATURE: [vmoperator](https://docs.victoriametrics.com/operator/): use `operator_bad_objects_total` metric with `object_namespace` and `crd` labels to track invalid objects managed by VMAgent, VMAuth, VMAlert and VMAlertmanager. Old `operator_alertmanager_bad_objects_count` and `operator_vmalert_bad_objects_count` are deprecated and will be removed in next releases. * FEATURE: [vmoperator](https://docs.victoriametrics.com/operator/): added HPA support for all cluster CR storage. See [#1678](https://github.com/VictoriaMetrics/operator/issues/1678). +* FEATURE: [vmsingle](https://docs.victoriametrics.com/operator/resources/vmsingle/): support relabelling and scraping on VMSingle. * BUGFIX: [vmoperator](https://docs.victoriametrics.com/operator/): fixed HPA cleanup logic for all cluster resources, before it was constantly recreated. Bug introduced in [this commit](https://github.com/VictoriaMetrics/operator/commit/983d1678c37497a7d03d2f57821219fd4975deec). * BUGFIX: [VMCluster](https://docs.victoriametrics.com/operator/resources/vmcluster/), [VLCluster](https://docs.victoriametrics.com/operator/resources/vlcluster/) and [VTCluster](https://docs.victoriametrics.com/operator/resources/vtcluster/): prevent cluster load balancer secret from infinite reconcile. diff --git a/docs/api.md b/docs/api.md index 27918fc11..874119353 100644 --- a/docs/api.md +++ b/docs/api.md @@ -1182,7 +1182,7 @@ Package v1beta1 contains API Schema definitions for the victoriametrics v1beta1 APIServerConfig defines a host and auth methods to access apiserver. -Appears in: [VMAgentSpec](#vmagentspec) +Appears in: [VMAgentSpec](#vmagentspec), [VMSingleSpec](#vmsinglespec) | Field | Description | | --- | --- | @@ -1287,7 +1287,7 @@ in the vmagent container. Those secrets would then be sent with a scrape request by vmagent to a malicious target. Denying the above would prevent the attack, users can instead use the BearerTokenSecret field. -Appears in: [CommonScrapeParams](#commonscrapeparams), [CommonScrapeSecurityEnforcements](#commonscrapesecurityenforcements), [VMAgentSpec](#vmagentspec) +Appears in: [CommonScrapeParams](#commonscrapeparams), [CommonScrapeSecurityEnforcements](#commonscrapesecurityenforcements), [VMAgentSpec](#vmagentspec), [VMSingleSpec](#vmsinglespec) | Field | Description | | --- | --- | @@ -1468,7 +1468,7 @@ Appears in: [VLAgentSpec](#vlagentspec), [VLInsert](#vlinsert), [VLSelect](#vlse -Appears in: [VMAgentSpec](#vmagentspec), [VMAlertSpec](#vmalertspec), [VMAlertmanagerSpec](#vmalertmanagerspec), [VMAuthSpec](#vmauthspec) +Appears in: [VMAgentSpec](#vmagentspec), [VMAlertSpec](#vmalertspec), [VMAlertmanagerSpec](#vmalertmanagerspec), [VMAuthSpec](#vmauthspec), [VMSingleSpec](#vmsinglespec) | Field | Description | | --- | --- | @@ -1505,7 +1505,7 @@ Appears in: [VLAgentSpec](#vlagentspec), [VLInsert](#vlinsert), [VLSelect](#vlse CommonRelabelParams defines params for relabelling -Appears in: [VMAgentSpec](#vmagentspec) +Appears in: [VMAgentSpec](#vmagentspec), [VMSingleSpec](#vmsinglespec) | Field | Description | | --- | --- | @@ -1519,12 +1519,12 @@ Appears in: [VMAgentSpec](#vmagentspec) -Appears in: [VMAgentSpec](#vmagentspec) +Appears in: [VMAgentSpec](#vmagentspec), [VMSingleSpec](#vmsinglespec) | Field | Description | | --- | --- | -| additionalScrapeConfigs#
_[SecretKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#secretkeyselector-v1-core)_ | _(Optional)_
AdditionalScrapeConfigs 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. | -| arbitraryFSAccessThroughSMs#
_[ArbitraryFSAccessThroughSMsConfig](#arbitraryfsaccessthroughsmsconfig)_ | _(Optional)_
ArbitraryFSAccessThroughSMs configures whether configuration
based on EndpointAuth can access arbitrary files on the file system
of the VMAgent container e.g. bearer token files, basic auth, tls certs | +| additionalScrapeConfigs#
_[SecretKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#secretkeyselector-v1-core)_ | _(Optional)_
AdditionalScrapeConfigs 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 or VMSingle. It is advised to review VMAgent or VMSingle release
notes to ensure that no incompatible scrape configs are going to break
VMAgent or VMSingle after the upgrade. | +| arbitraryFSAccessThroughSMs#
_[ArbitraryFSAccessThroughSMsConfig](#arbitraryfsaccessthroughsmsconfig)_ | _(Optional)_
ArbitraryFSAccessThroughSMs configures whether configuration
based on EndpointAuth can access arbitrary files on the file system
of the VMAgent or VMSingle container e.g. bearer token files, basic auth, tls certs | | enableKubernetesAPISelectors#
_boolean_ | _(Optional)_
EnableKubernetesAPISelectors instructs vmagent to use CRD scrape objects spec.selectors for
Kubernetes API list and watch requests.
https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#list-and-watch-filtering
It could be useful to reduce Kubernetes API server resource usage for serving less than 100 CRD scrape objects in total. | | enforcedNamespaceLabel#
_string_ | _(Optional)_
EnforcedNamespaceLabel enforces adding a namespace label of origin for each alert
and metric that is user created. The label value will always be the namespace of the object that is
being created. | | externalLabelName#
_string_ | _(Optional)_
ExternalLabelName Name of external label used to denote scraping agent instance
name. Defaults to the value of `prometheus`. External label will
_not_ be added when value is set to empty string (`""`). | @@ -1532,34 +1532,35 @@ Appears in: [VMAgentSpec](#vmagentspec) | globalScrapeMetricRelabelConfigs#
_[RelabelConfig](#relabelconfig) array_ | _(Optional)_
GlobalScrapeMetricRelabelConfigs is a global metric relabel configuration, which is applied to each scrape job. | | globalScrapeRelabelConfigs#
_[RelabelConfig](#relabelconfig) array_ | _(Optional)_
GlobalScrapeRelabelConfigs is a global relabel configuration, which is applied to each samples of each scrape job during service discovery. | | ignoreNamespaceSelectors#
_boolean_ | _(Optional)_
IgnoreNamespaceSelectors if set to true will ignore NamespaceSelector settings from
scrape objects, and they will only discover endpoints
within their current namespace. Defaults to false. | -| 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"] | +| ingestOnlyMode#
_boolean_ | _(Optional)_
IngestOnlyMode switches vmagent or vmsingle into unmanaged mode
it disables any config generation for scraping
Currently it prevents vmagent from managing tls and auth options for remote write | +| 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 or VMSingle. It is advised to review VMAgent or VMSingle release
notes to ensure that no incompatible scrape configs are going to break
VMAgent or VMSingle after the upgrade.
it should be defined as single yaml file.
inlineScrapeConfig: \|
- job_name: "prometheus"
static_configs:
- targets: ["localhost:9090"] | | maxScrapeInterval#
_string_ | _(Required)_
MaxScrapeInterval allows limiting maximum scrape interval for VMServiceScrape, VMPodScrape and other scrapes
If interval is higher than defined limit, `maxScrapeInterval` will be used. | | minScrapeInterval#
_string_ | _(Required)_
MinScrapeInterval allows limiting minimal scrape interval for VMServiceScrape, VMPodScrape and other scrapes
If interval is lower than defined limit, `minScrapeInterval` will be used. | -| nodeScrapeNamespaceSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | _(Optional)_
NodeScrapeNamespaceSelector defines Namespaces to be selected for VMNodeScrape discovery.
Works in combination with Selector.
NamespaceSelector nil - only objects at VMAgent namespace.
Selector nil - only objects at NamespaceSelector namespaces.
If both nil - behaviour controlled by selectAllByDefault | +| nodeScrapeNamespaceSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | _(Optional)_
NodeScrapeNamespaceSelector defines Namespaces to be selected for VMNodeScrape discovery.
Works in combination with Selector.
NamespaceSelector nil - only objects at VMAgent or VMSingle namespace.
Selector nil - only objects at NamespaceSelector namespaces.
If both nil - behaviour controlled by selectAllByDefault | | nodeScrapeRelabelTemplate#
_[RelabelConfig](#relabelconfig) array_ | _(Optional)_
NodeScrapeRelabelTemplate defines relabel config, that will be added to each VMNodeScrape.
it's useful for adding specific labels to all targets | -| nodeScrapeSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | _(Optional)_
NodeScrapeSelector defines VMNodeScrape to be selected for scraping.
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 | +| nodeScrapeSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | _(Optional)_
NodeScrapeSelector defines VMNodeScrape to be selected for scraping.
Works in combination with NamespaceSelector.
NamespaceSelector nil - only objects at VMAgent or VMSingle namespace.
Selector nil - only objects at NamespaceSelector namespaces.
If both nil - behaviour controlled by selectAllByDefault | | overrideHonorLabels#
_boolean_ | _(Optional)_
OverrideHonorLabels if set to true overrides all user configured honor_labels.
If HonorLabels is set in scrape objects to true, this overrides honor_labels to false. | | overrideHonorTimestamps#
_boolean_ | _(Optional)_
OverrideHonorTimestamps allows to globally enforce honoring timestamps in all scrape configs. | -| podScrapeNamespaceSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | _(Optional)_
PodScrapeNamespaceSelector defines Namespaces to be selected for VMPodScrape discovery.
Works in combination with Selector.
NamespaceSelector nil - only objects at VMAgent namespace.
Selector nil - only objects at NamespaceSelector namespaces.
If both nil - behaviour controlled by selectAllByDefault | +| podScrapeNamespaceSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | _(Optional)_
PodScrapeNamespaceSelector defines Namespaces to be selected for VMPodScrape discovery.
Works in combination with Selector.
NamespaceSelector nil - only objects at VMAgent or VMSingle namespace.
Selector nil - only objects at NamespaceSelector namespaces.
If both nil - behaviour controlled by selectAllByDefault | | podScrapeRelabelTemplate#
_[RelabelConfig](#relabelconfig) array_ | _(Optional)_
PodScrapeRelabelTemplate defines relabel config, that will be added to each VMPodScrape.
it's useful for adding specific labels to all targets | -| podScrapeSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | _(Optional)_
PodScrapeSelector defines PodScrapes to be selected for target 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 | -| probeNamespaceSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | _(Optional)_
ProbeNamespaceSelector defines Namespaces to be selected for VMProbe discovery.
Works in combination with Selector.
NamespaceSelector nil - only objects at VMAgent namespace.
Selector nil - only objects at NamespaceSelector namespaces.
If both nil - behaviour controlled by selectAllByDefault | +| podScrapeSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | _(Optional)_
PodScrapeSelector defines PodScrapes to be selected for target discovery.
Works in combination with NamespaceSelector.
NamespaceSelector nil - only objects at VMAgent or VMSingle namespace.
Selector nil - only objects at NamespaceSelector namespaces.
If both nil - behaviour controlled by selectAllByDefault | +| probeNamespaceSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | _(Optional)_
ProbeNamespaceSelector defines Namespaces to be selected for VMProbe discovery.
Works in combination with Selector.
NamespaceSelector nil - only objects at VMAgent or VMSingle namespace.
Selector nil - only objects at NamespaceSelector namespaces.
If both nil - behaviour controlled by selectAllByDefault | | 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.30/#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 | +| probeSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | _(Optional)_
ProbeSelector defines VMProbe to be selected for target probing.
Works in combination with NamespaceSelector.
NamespaceSelector nil - only objects at VMAgent or VMSingle namespace.
Selector nil - only objects at NamespaceSelector namespaces.
If both nil - behaviour controlled by selectAllByDefault | | sampleLimit#
_integer_ | _(Optional)_
SampleLimit defines global per target limit of scraped samples | | scrapeClasses#
_[ScrapeClass](#scrapeclass) array_ | _(Optional)_
ScrapeClasses defines the list of scrape classes to expose to scraping objects such as
PodScrapes, ServiceScrapes, Probes and ScrapeConfigs. | -| scrapeConfigNamespaceSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | _(Optional)_
ScrapeConfigNamespaceSelector defines Namespaces to be selected for VMScrapeConfig discovery.
Works in combination with Selector.
NamespaceSelector nil - only objects at VMAgent namespace.
Selector nil - only objects at NamespaceSelector namespaces.
If both nil - behaviour controlled by selectAllByDefault | +| scrapeConfigNamespaceSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | _(Optional)_
ScrapeConfigNamespaceSelector defines Namespaces to be selected for VMScrapeConfig discovery.
Works in combination with Selector.
NamespaceSelector nil - only objects at VMAgent or VMSingle namespace.
Selector nil - only objects at NamespaceSelector namespaces.
If both nil - behaviour controlled by selectAllByDefault | | scrapeConfigRelabelTemplate#
_[RelabelConfig](#relabelconfig) array_ | _(Optional)_
ScrapeConfigRelabelTemplate defines relabel config, that will be added to each VMScrapeConfig.
it's useful for adding specific labels to all targets | | scrapeConfigSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | _(Optional)_
ScrapeConfigSelector defines VMScrapeConfig to be selected for target discovery.
Works in combination with NamespaceSelector. | | scrapeInterval#
_string_ | _(Optional)_
ScrapeInterval defines how often scrape targets by default | | scrapeTimeout#
_string_ | _(Optional)_
ScrapeTimeout defines global timeout for targets scrape | | selectAllByDefault#
_boolean_ | _(Optional)_
SelectAllByDefault changes default behavior for empty CRD selectors, such ServiceScrapeSelector.
with selectAllByDefault: true and empty serviceScrapeSelector and ServiceScrapeNamespaceSelector
Operator selects all exist serviceScrapes
with selectAllByDefault: false - selects nothing | -| serviceScrapeNamespaceSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | _(Optional)_
ServiceScrapeNamespaceSelector Namespaces to be selected for VMServiceScrape discovery.
Works in combination with Selector.
NamespaceSelector nil - only objects at VMAgent namespace.
Selector nil - only objects at NamespaceSelector namespaces.
If both nil - behaviour controlled by selectAllByDefault | +| serviceScrapeNamespaceSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | _(Optional)_
ServiceScrapeNamespaceSelector Namespaces to be selected for VMServiceScrape discovery.
Works in combination with Selector.
NamespaceSelector nil - only objects at VMAgent or VMSingle namespace.
Selector nil - only objects at NamespaceSelector namespaces.
If both nil - behaviour controlled by selectAllByDefault | | serviceScrapeRelabelTemplate#
_[RelabelConfig](#relabelconfig) array_ | _(Optional)_
ServiceScrapeRelabelTemplate defines relabel config, that will be added to each VMServiceScrape.
it's useful for adding specific labels to all targets | -| serviceScrapeSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | _(Optional)_
ServiceScrapeSelector defines ServiceScrapes to be selected for target 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 | -| staticScrapeNamespaceSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#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 | +| serviceScrapeSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | _(Optional)_
ServiceScrapeSelector defines ServiceScrapes to be selected for target discovery.
Works in combination with NamespaceSelector.
NamespaceSelector nil - only objects at VMAgent or VMSingle namespace.
Selector nil - only objects at NamespaceSelector namespaces.
If both nil - behaviour controlled by selectAllByDefault | +| staticScrapeNamespaceSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | _(Optional)_
StaticScrapeNamespaceSelector defines Namespaces to be selected for VMStaticScrape discovery.
Works in combination with NamespaceSelector.
NamespaceSelector nil - only objects at VMAgent or VMSingle 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 | -| staticScrapeSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | _(Optional)_
StaticScrapeSelector defines VMStaticScrape to be selected for target discovery.
Works in combination with NamespaceSelector.
If both nil - match everything.
NamespaceSelector nil - only objects at VMAgent namespace.
Selector nil - only objects at NamespaceSelector namespaces. | +| staticScrapeSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | _(Optional)_
StaticScrapeSelector defines VMStaticScrape to be selected for target discovery.
Works in combination with NamespaceSelector.
If both nil - match everything.
NamespaceSelector nil - only objects at VMAgent or VMSingle namespace.
Selector nil - only objects at NamespaceSelector namespaces. | | vmAgentExternalLabelName#
_string_ | _(Optional)_
VMAgentExternalLabelName Name of vmAgent external label used to denote vmAgent instance
name. Defaults to the value of `prometheus`. External label will
_not_ be added when value is set to empty string (`""`).
Deprecated: use externalLabelName instead. will be removed in v0.69.0 | @@ -1569,11 +1570,11 @@ Appears in: [VMAgentSpec](#vmagentspec) CommonScrapeSecurityEnforcements defines security configuration for endpoint scrapping -Appears in: [CommonScrapeParams](#commonscrapeparams), [VMAgentSpec](#vmagentspec) +Appears in: [CommonScrapeParams](#commonscrapeparams), [VMAgentSpec](#vmagentspec), [VMSingleSpec](#vmsinglespec) | Field | Description | | --- | --- | -| arbitraryFSAccessThroughSMs#
_[ArbitraryFSAccessThroughSMsConfig](#arbitraryfsaccessthroughsmsconfig)_ | _(Optional)_
ArbitraryFSAccessThroughSMs configures whether configuration
based on EndpointAuth can access arbitrary files on the file system
of the VMAgent container e.g. bearer token files, basic auth, tls certs | +| arbitraryFSAccessThroughSMs#
_[ArbitraryFSAccessThroughSMsConfig](#arbitraryfsaccessthroughsmsconfig)_ | _(Optional)_
ArbitraryFSAccessThroughSMs configures whether configuration
based on EndpointAuth can access arbitrary files on the file system
of the VMAgent or VMSingle container e.g. bearer token files, basic auth, tls certs | | enforcedNamespaceLabel#
_string_ | _(Optional)_
EnforcedNamespaceLabel enforces adding a namespace label of origin for each alert
and metric that is user created. The label value will always be the namespace of the object that is
being created. | | ignoreNamespaceSelectors#
_boolean_ | _(Optional)_
IgnoreNamespaceSelectors if set to true will ignore NamespaceSelector settings from
scrape objects, and they will only discover endpoints
within their current namespace. Defaults to false. | | overrideHonorLabels#
_boolean_ | _(Optional)_
OverrideHonorLabels if set to true overrides all user configured honor_labels.
If HonorLabels is set in scrape objects to true, this overrides honor_labels to false. | @@ -2668,7 +2669,7 @@ Appears in: [VMAlertmanagerConfigSpec](#vmalertmanagerconfigspec) RelabelConfig allows dynamic rewriting of the label set More info: https://docs.victoriametrics.com/victoriametrics/#relabeling -Appears in: [CommonRelabelParams](#commonrelabelparams), [CommonScrapeParams](#commonscrapeparams), [Endpoint](#endpoint), [EndpointRelabelings](#endpointrelabelings), [PodMetricsEndpoint](#podmetricsendpoint), [ProbeTargetIngress](#probetargetingress), [ScrapeClass](#scrapeclass), [StreamAggrRule](#streamaggrrule), [TargetEndpoint](#targetendpoint), [VMAgentRemoteWriteSpec](#vmagentremotewritespec), [VMAgentSpec](#vmagentspec), [VMNodeScrapeSpec](#vmnodescrapespec), [VMProbeSpec](#vmprobespec), [VMProbeTargetStaticConfig](#vmprobetargetstaticconfig), [VMScrapeConfigSpec](#vmscrapeconfigspec) +Appears in: [CommonRelabelParams](#commonrelabelparams), [CommonScrapeParams](#commonscrapeparams), [Endpoint](#endpoint), [EndpointRelabelings](#endpointrelabelings), [PodMetricsEndpoint](#podmetricsendpoint), [ProbeTargetIngress](#probetargetingress), [ScrapeClass](#scrapeclass), [StreamAggrRule](#streamaggrrule), [TargetEndpoint](#targetendpoint), [VMAgentRemoteWriteSpec](#vmagentremotewritespec), [VMAgentSpec](#vmagentspec), [VMNodeScrapeSpec](#vmnodescrapespec), [VMProbeSpec](#vmprobespec), [VMProbeTargetStaticConfig](#vmprobetargetstaticconfig), [VMScrapeConfigSpec](#vmscrapeconfigspec), [VMSingleSpec](#vmsinglespec) | Field | Description | | --- | --- | @@ -2828,7 +2829,7 @@ Appears in: [VMRuleSpec](#vmrulespec) -Appears in: [CommonScrapeParams](#commonscrapeparams), [VMAgentSpec](#vmagentspec) +Appears in: [CommonScrapeParams](#commonscrapeparams), [VMAgentSpec](#vmagentspec), [VMSingleSpec](#vmsinglespec) | Field | Description | | --- | --- | @@ -3541,7 +3542,7 @@ Appears in: [VMAgentSpec](#vmagentspec) | streamAggrConfig#
_[StreamAggrConfig](#streamaggrconfig)_ | _(Optional)_
StreamAggrConfig defines stream aggregation configuration for VMAgent for -remoteWrite.url | | tlsConfig#
_[TLSConfig](#tlsconfig)_ | _(Optional)_
TLSConfig describes tls configuration for remote write target | | url#
_string_ | _(Required)_
URL of the endpoint to send samples to. | -| urlRelabelConfig#
_[ConfigMapKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#configmapkeyselector-v1-core)_ | _(Optional)_
ConfigMap with relabeling config which is applied to metrics before sending them to the corresponding -remoteWrite.url | +| urlRelabelConfig#
_[ConfigMapKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#configmapkeyselector-v1-core)_ | _(Optional)_
ConfigMap with relabeling config which is applied to metrics before sending them to the corresponding -remoteWrite.url. | #### VMAgentSpec @@ -3554,10 +3555,10 @@ Appears in: [VMAgent](#vmagent) | Field | Description | | --- | --- | -| additionalScrapeConfigs#
_[SecretKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#secretkeyselector-v1-core)_ | _(Optional)_
AdditionalScrapeConfigs 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. | +| additionalScrapeConfigs#
_[SecretKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#secretkeyselector-v1-core)_ | _(Optional)_
AdditionalScrapeConfigs 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 or VMSingle. It is advised to review VMAgent or VMSingle release
notes to ensure that no incompatible scrape configs are going to break
VMAgent or VMSingle after the upgrade. | | affinity#
_[Affinity](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#affinity-v1-core)_ | _(Optional)_
Affinity If specified, the pod's scheduling constraints. | | apiServerConfig#
_[APIServerConfig](#apiserverconfig)_ | _(Optional)_
APIServerConfig allows specifying a host and auth methods to access apiserver.
If left empty, VMAgent is assumed to run inside of the cluster
and will discover API servers automatically and use the pod's CA certificate
and bearer token file at /var/run/secrets/kubernetes.io/serviceaccount/. | -| arbitraryFSAccessThroughSMs#
_[ArbitraryFSAccessThroughSMsConfig](#arbitraryfsaccessthroughsmsconfig)_ | _(Optional)_
ArbitraryFSAccessThroughSMs configures whether configuration
based on EndpointAuth can access arbitrary files on the file system
of the VMAgent container e.g. bearer token files, basic auth, tls certs | +| arbitraryFSAccessThroughSMs#
_[ArbitraryFSAccessThroughSMsConfig](#arbitraryfsaccessthroughsmsconfig)_ | _(Optional)_
ArbitraryFSAccessThroughSMs configures whether configuration
based on EndpointAuth can access arbitrary files on the file system
of the VMAgent or VMSingle container e.g. bearer token files, basic auth, tls certs | | claimTemplates#
_[PersistentVolumeClaim](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#persistentvolumeclaim-v1-core) array_ | _(Required)_
ClaimTemplates allows adding additional VolumeClaimTemplates for VMAgent in StatefulMode | | 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.30/#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 | @@ -3586,10 +3587,10 @@ Appears in: [VMAgent](#vmagent) | ignoreNamespaceSelectors#
_boolean_ | _(Optional)_
IgnoreNamespaceSelectors if set to true will ignore NamespaceSelector settings from
scrape objects, and they will only discover endpoints
within their current namespace. Defaults to false. | | 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.30/#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 | -| ingestOnlyMode#
_boolean_ | _(Optional)_
IngestOnlyMode switches vmagent into unmanaged mode
it disables any config generation for scraping
Currently it prevents vmagent from managing tls and auth options for remote write | +| ingestOnlyMode#
_boolean_ | _(Optional)_
IngestOnlyMode switches vmagent or vmsingle into unmanaged mode
it disables any config generation for scraping
Currently it prevents vmagent from managing tls and auth options for remote write | | initContainers#
_[Container](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#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/ | | inlineRelabelConfig#
_[RelabelConfig](#relabelconfig) array_ | _(Optional)_
InlineRelabelConfig - defines GlobalRelabelConfig for vmagent, can be defined directly at CRD. | -| 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"] | +| 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 or VMSingle. It is advised to review VMAgent or VMSingle release
notes to ensure that no incompatible scrape configs are going to break
VMAgent or VMSingle 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/) | | logFormat#
_string_ | _(Optional)_
LogFormat for VMAgent to be configured with. | @@ -3598,9 +3599,9 @@ Appears in: [VMAgent](#vmagent) | maxScrapeInterval#
_string_ | _(Required)_
MaxScrapeInterval allows limiting maximum scrape interval for VMServiceScrape, VMPodScrape and other scrapes
If interval is higher than defined limit, `maxScrapeInterval` will be used. | | 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 | | minScrapeInterval#
_string_ | _(Required)_
MinScrapeInterval allows limiting minimal scrape interval for VMServiceScrape, VMPodScrape and other scrapes
If interval is lower than defined limit, `minScrapeInterval` will be used. | -| nodeScrapeNamespaceSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | _(Optional)_
NodeScrapeNamespaceSelector defines Namespaces to be selected for VMNodeScrape discovery.
Works in combination with Selector.
NamespaceSelector nil - only objects at VMAgent namespace.
Selector nil - only objects at NamespaceSelector namespaces.
If both nil - behaviour controlled by selectAllByDefault | +| nodeScrapeNamespaceSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | _(Optional)_
NodeScrapeNamespaceSelector defines Namespaces to be selected for VMNodeScrape discovery.
Works in combination with Selector.
NamespaceSelector nil - only objects at VMAgent or VMSingle namespace.
Selector nil - only objects at NamespaceSelector namespaces.
If both nil - behaviour controlled by selectAllByDefault | | nodeScrapeRelabelTemplate#
_[RelabelConfig](#relabelconfig) array_ | _(Optional)_
NodeScrapeRelabelTemplate defines relabel config, that will be added to each VMNodeScrape.
it's useful for adding specific labels to all targets | -| nodeScrapeSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | _(Optional)_
NodeScrapeSelector defines VMNodeScrape to be selected for scraping.
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 | +| nodeScrapeSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | _(Optional)_
NodeScrapeSelector defines VMNodeScrape to be selected for scraping.
Works in combination with NamespaceSelector.
NamespaceSelector nil - only objects at VMAgent or VMSingle namespace.
Selector nil - only objects at NamespaceSelector namespaces.
If both nil - behaviour controlled by selectAllByDefault | | nodeSelector#
_object (keys:string, values:string)_ | _(Optional)_
NodeSelector Define which Nodes the Pods are scheduled on. | | overrideHonorLabels#
_boolean_ | _(Optional)_
OverrideHonorLabels if set to true overrides all user configured honor_labels.
If HonorLabels is set in scrape objects to true, this overrides honor_labels to false. | | overrideHonorTimestamps#
_boolean_ | _(Optional)_
OverrideHonorTimestamps allows to globally enforce honoring timestamps in all scrape configs. | @@ -3608,14 +3609,14 @@ Appears in: [VMAgent](#vmagent) | persistentVolumeClaimRetentionPolicy#
_[StatefulSetPersistentVolumeClaimRetentionPolicy](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#statefulsetpersistentvolumeclaimretentionpolicy-v1-apps)_ | _(Optional)_
PersistentVolumeClaimRetentionPolicy allows configuration of PVC retention policy | | podDisruptionBudget#
_[EmbeddedPodDisruptionBudgetSpec](#embeddedpoddisruptionbudgetspec)_ | _(Optional)_
PodDisruptionBudget created by operator | | podMetadata#
_[EmbeddedObjectMetadata](#embeddedobjectmetadata)_ | _(Optional)_
PodMetadata configures Labels and Annotations which are propagated to the vmagent pods. | -| podScrapeNamespaceSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | _(Optional)_
PodScrapeNamespaceSelector defines Namespaces to be selected for VMPodScrape discovery.
Works in combination with Selector.
NamespaceSelector nil - only objects at VMAgent namespace.
Selector nil - only objects at NamespaceSelector namespaces.
If both nil - behaviour controlled by selectAllByDefault | +| podScrapeNamespaceSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | _(Optional)_
PodScrapeNamespaceSelector defines Namespaces to be selected for VMPodScrape discovery.
Works in combination with Selector.
NamespaceSelector nil - only objects at VMAgent or VMSingle namespace.
Selector nil - only objects at NamespaceSelector namespaces.
If both nil - behaviour controlled by selectAllByDefault | | podScrapeRelabelTemplate#
_[RelabelConfig](#relabelconfig) array_ | _(Optional)_
PodScrapeRelabelTemplate defines relabel config, that will be added to each VMPodScrape.
it's useful for adding specific labels to all targets | -| podScrapeSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | _(Optional)_
PodScrapeSelector defines PodScrapes to be selected for target 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 | +| podScrapeSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | _(Optional)_
PodScrapeSelector defines PodScrapes to be selected for target discovery.
Works in combination with NamespaceSelector.
NamespaceSelector nil - only objects at VMAgent or VMSingle namespace.
Selector nil - only objects at NamespaceSelector namespaces.
If both nil - behaviour controlled by selectAllByDefault | | port#
_string_ | _(Optional)_
Port listen address | | priorityClassName#
_string_ | _(Optional)_
PriorityClassName class assigned to the Pods | -| probeNamespaceSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | _(Optional)_
ProbeNamespaceSelector defines Namespaces to be selected for VMProbe discovery.
Works in combination with Selector.
NamespaceSelector nil - only objects at VMAgent namespace.
Selector nil - only objects at NamespaceSelector namespaces.
If both nil - behaviour controlled by selectAllByDefault | +| probeNamespaceSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | _(Optional)_
ProbeNamespaceSelector defines Namespaces to be selected for VMProbe discovery.
Works in combination with Selector.
NamespaceSelector nil - only objects at VMAgent or VMSingle namespace.
Selector nil - only objects at NamespaceSelector namespaces.
If both nil - behaviour controlled by selectAllByDefault | | 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.30/#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 | +| probeSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | _(Optional)_
ProbeSelector defines VMProbe to be selected for target probing.
Works in combination with NamespaceSelector.
NamespaceSelector nil - only objects at VMAgent or VMSingle 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.30/#podreadinessgate-v1-core) array_ | _(Required)_
ReadinessGates defines pod readiness gates | | relabelConfig#
_[ConfigMapKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#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 | @@ -3628,7 +3629,7 @@ Appears in: [VMAgent](#vmagent) | sampleLimit#
_integer_ | _(Optional)_
SampleLimit defines global per target limit of scraped samples | | schedulerName#
_string_ | _(Optional)_
SchedulerName - defines kubernetes scheduler name | | scrapeClasses#
_[ScrapeClass](#scrapeclass) array_ | _(Optional)_
ScrapeClasses defines the list of scrape classes to expose to scraping objects such as
PodScrapes, ServiceScrapes, Probes and ScrapeConfigs. | -| scrapeConfigNamespaceSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | _(Optional)_
ScrapeConfigNamespaceSelector defines Namespaces to be selected for VMScrapeConfig discovery.
Works in combination with Selector.
NamespaceSelector nil - only objects at VMAgent namespace.
Selector nil - only objects at NamespaceSelector namespaces.
If both nil - behaviour controlled by selectAllByDefault | +| scrapeConfigNamespaceSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | _(Optional)_
ScrapeConfigNamespaceSelector defines Namespaces to be selected for VMScrapeConfig discovery.
Works in combination with Selector.
NamespaceSelector nil - only objects at VMAgent or VMSingle namespace.
Selector nil - only objects at NamespaceSelector namespaces.
If both nil - behaviour controlled by selectAllByDefault | | scrapeConfigRelabelTemplate#
_[RelabelConfig](#relabelconfig) array_ | _(Optional)_
ScrapeConfigRelabelTemplate defines relabel config, that will be added to each VMScrapeConfig.
it's useful for adding specific labels to all targets | | scrapeConfigSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | _(Optional)_
ScrapeConfigSelector defines VMScrapeConfig to be selected for target discovery.
Works in combination with NamespaceSelector. | | scrapeInterval#
_string_ | _(Optional)_
ScrapeInterval defines how often scrape targets by default | @@ -3637,18 +3638,18 @@ Appears in: [VMAgent](#vmagent) | securityContext#
_[SecurityContext](#securitycontext)_ | _(Optional)_
SecurityContext holds pod-level security attributes and common container settings.
This defaults to the default PodSecurityContext. | | selectAllByDefault#
_boolean_ | _(Optional)_
SelectAllByDefault changes default behavior for empty CRD selectors, such ServiceScrapeSelector.
with selectAllByDefault: true and empty serviceScrapeSelector and ServiceScrapeNamespaceSelector
Operator selects all exist serviceScrapes
with selectAllByDefault: false - selects nothing | | serviceAccountName#
_string_ | _(Optional)_
ServiceAccountName is the name of the ServiceAccount to use to run the pods | -| serviceScrapeNamespaceSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | _(Optional)_
ServiceScrapeNamespaceSelector Namespaces to be selected for VMServiceScrape discovery.
Works in combination with Selector.
NamespaceSelector nil - only objects at VMAgent namespace.
Selector nil - only objects at NamespaceSelector namespaces.
If both nil - behaviour controlled by selectAllByDefault | +| serviceScrapeNamespaceSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | _(Optional)_
ServiceScrapeNamespaceSelector Namespaces to be selected for VMServiceScrape discovery.
Works in combination with Selector.
NamespaceSelector nil - only objects at VMAgent or VMSingle namespace.
Selector nil - only objects at NamespaceSelector namespaces.
If both nil - behaviour controlled by selectAllByDefault | | serviceScrapeRelabelTemplate#
_[RelabelConfig](#relabelconfig) array_ | _(Optional)_
ServiceScrapeRelabelTemplate defines relabel config, that will be added to each VMServiceScrape.
it's useful for adding specific labels to all targets | -| serviceScrapeSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | _(Optional)_
ServiceScrapeSelector defines ServiceScrapes to be selected for target 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 | +| serviceScrapeSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | _(Optional)_
ServiceScrapeSelector defines ServiceScrapes to be selected for target discovery.
Works in combination with NamespaceSelector.
NamespaceSelector nil - only objects at VMAgent or VMSingle namespace.
Selector nil - only objects at NamespaceSelector namespaces.
If both nil - behaviour controlled by selectAllByDefault | | 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) | | 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.30/#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 | -| staticScrapeNamespaceSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#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 | +| staticScrapeNamespaceSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | _(Optional)_
StaticScrapeNamespaceSelector defines Namespaces to be selected for VMStaticScrape discovery.
Works in combination with NamespaceSelector.
NamespaceSelector nil - only objects at VMAgent or VMSingle 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 | -| staticScrapeSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | _(Optional)_
StaticScrapeSelector defines VMStaticScrape to be selected for target discovery.
Works in combination with NamespaceSelector.
If both nil - match everything.
NamespaceSelector nil - only objects at VMAgent namespace.
Selector nil - only objects at NamespaceSelector namespaces. | +| staticScrapeSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | _(Optional)_
StaticScrapeSelector defines VMStaticScrape to be selected for target discovery.
Works in combination with NamespaceSelector.
If both nil - match everything.
NamespaceSelector nil - only objects at VMAgent or VMSingle namespace.
Selector nil - only objects at NamespaceSelector namespaces. | | streamAggrConfig#
_[StreamAggrConfig](#streamaggrconfig)_ | _(Optional)_
StreamAggrConfig defines global stream aggregation configuration for VMAgent | | 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. | @@ -4785,46 +4786,92 @@ Appears in: [VMSingle](#vmsingle) | Field | Description | | --- | --- | +| additionalScrapeConfigs#
_[SecretKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#secretkeyselector-v1-core)_ | _(Optional)_
AdditionalScrapeConfigs 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 or VMSingle. It is advised to review VMAgent or VMSingle release
notes to ensure that no incompatible scrape configs are going to break
VMAgent or VMSingle after the upgrade. | | affinity#
_[Affinity](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#affinity-v1-core)_ | _(Optional)_
Affinity If specified, the pod's scheduling constraints. | +| apiServerConfig#
_[APIServerConfig](#apiserverconfig)_ | _(Optional)_
APIServerConfig allows specifying a host and auth methods to access apiserver.
If left empty, VMSingle is assumed to run inside of the cluster
and will discover API servers automatically and use the pod's CA certificate
and bearer token file at /var/run/secrets/kubernetes.io/serviceaccount/. | +| arbitraryFSAccessThroughSMs#
_[ArbitraryFSAccessThroughSMsConfig](#arbitraryfsaccessthroughsmsconfig)_ | _(Optional)_
ArbitraryFSAccessThroughSMs configures whether configuration
based on EndpointAuth can access arbitrary files on the file system
of the VMAgent or VMSingle container e.g. bearer token files, basic auth, tls certs | | 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.30/#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 resyncInterval: "30s" | +| configReloaderImage#
_string_ | _(Optional)_
ConfigReloaderImage defines image:tag for config-reloader container | +| configReloaderImageTag#
_string_ | _(Optional)_
ConfigReloaderImageTag defines image:tag for config-reloader container
Deprecated: use configReloaderImage instead | +| configReloaderResources#
_[ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#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.30/#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.30/#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.30/#dnspolicy-v1-core)_ | _(Optional)_
DNSPolicy sets DNS policy for the pod | +| enableKubernetesAPISelectors#
_boolean_ | _(Optional)_
EnableKubernetesAPISelectors instructs vmagent to use CRD scrape objects spec.selectors for
Kubernetes API list and watch requests.
https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#list-and-watch-filtering
It could be useful to reduce Kubernetes API server resource usage for serving less than 100 CRD scrape objects in total. | +| enforcedNamespaceLabel#
_string_ | _(Optional)_
EnforcedNamespaceLabel enforces adding a namespace label of origin for each alert
and metric that is user created. The label value will always be the namespace of the object that is
being created. | +| externalLabelName#
_string_ | _(Optional)_
ExternalLabelName Name of external label used to denote scraping agent instance
name. Defaults to the value of `prometheus`. External label will
_not_ be added when value is set to empty string (`""`). | +| externalLabels#
_object (keys:string, values:string)_ | _(Optional)_
ExternalLabels The labels to add to any time series scraped by vmagent.
it doesn't affect metrics ingested directly by push API's | | 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.30/#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.30/#envfromsource-v1-core) array_ | _(Optional)_
ExtraEnvsFrom defines source of env variables for the application container
could either be secret or configmap | +| globalScrapeMetricRelabelConfigs#
_[RelabelConfig](#relabelconfig) array_ | _(Optional)_
GlobalScrapeMetricRelabelConfigs is a global metric relabel configuration, which is applied to each scrape job. | +| globalScrapeRelabelConfigs#
_[RelabelConfig](#relabelconfig) array_ | _(Optional)_
GlobalScrapeRelabelConfigs is a global relabel configuration, which is applied to each samples of each scrape job during service discovery. | | hostAliases#
_[HostAlias](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#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.30/#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 | +| ignoreNamespaceSelectors#
_boolean_ | _(Optional)_
IgnoreNamespaceSelectors if set to true will ignore NamespaceSelector settings from
scrape objects, and they will only discover endpoints
within their current namespace. Defaults to false. | | 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.30/#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 | +| ingestOnlyMode#
_boolean_ | _(Optional)_
IngestOnlyMode switches vmagent or vmsingle into unmanaged mode
it disables any config generation for scraping
Currently it prevents vmagent from managing tls and auth options for remote write | | initContainers#
_[Container](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#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/ | +| inlineRelabelConfig#
_[RelabelConfig](#relabelconfig) array_ | _(Optional)_
InlineRelabelConfig - defines GlobalRelabelConfig for vmagent, can be defined directly at CRD. | +| 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 or VMSingle. It is advised to review VMAgent or VMSingle release
notes to ensure that no incompatible scrape configs are going to break
VMAgent or VMSingle 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/) | | 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 | +| maxScrapeInterval#
_string_ | _(Required)_
MaxScrapeInterval allows limiting maximum scrape interval for VMServiceScrape, VMPodScrape and other scrapes
If interval is higher than defined limit, `maxScrapeInterval` will be used. | | 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 | +| minScrapeInterval#
_string_ | _(Required)_
MinScrapeInterval allows limiting minimal scrape interval for VMServiceScrape, VMPodScrape and other scrapes
If interval is lower than defined limit, `minScrapeInterval` will be used. | +| nodeScrapeNamespaceSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | _(Optional)_
NodeScrapeNamespaceSelector defines Namespaces to be selected for VMNodeScrape discovery.
Works in combination with Selector.
NamespaceSelector nil - only objects at VMAgent or VMSingle namespace.
Selector nil - only objects at NamespaceSelector namespaces.
If both nil - behaviour controlled by selectAllByDefault | +| nodeScrapeRelabelTemplate#
_[RelabelConfig](#relabelconfig) array_ | _(Optional)_
NodeScrapeRelabelTemplate defines relabel config, that will be added to each VMNodeScrape.
it's useful for adding specific labels to all targets | +| nodeScrapeSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | _(Optional)_
NodeScrapeSelector defines VMNodeScrape to be selected for scraping.
Works in combination with NamespaceSelector.
NamespaceSelector nil - only objects at VMAgent or VMSingle namespace.
Selector nil - only objects at NamespaceSelector namespaces.
If both nil - behaviour controlled by selectAllByDefault | | nodeSelector#
_object (keys:string, values:string)_ | _(Optional)_
NodeSelector Define which Nodes the Pods are scheduled on. | +| overrideHonorLabels#
_boolean_ | _(Optional)_
OverrideHonorLabels if set to true overrides all user configured honor_labels.
If HonorLabels is set in scrape objects to true, this overrides honor_labels to false. | +| overrideHonorTimestamps#
_boolean_ | _(Optional)_
OverrideHonorTimestamps allows to globally enforce honoring timestamps in all scrape configs. | | 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. | | podMetadata#
_[EmbeddedObjectMetadata](#embeddedobjectmetadata)_ | _(Optional)_
PodMetadata configures Labels and Annotations which are propagated to the VMSingle pods. | +| podScrapeNamespaceSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | _(Optional)_
PodScrapeNamespaceSelector defines Namespaces to be selected for VMPodScrape discovery.
Works in combination with Selector.
NamespaceSelector nil - only objects at VMAgent or VMSingle namespace.
Selector nil - only objects at NamespaceSelector namespaces.
If both nil - behaviour controlled by selectAllByDefault | +| podScrapeRelabelTemplate#
_[RelabelConfig](#relabelconfig) array_ | _(Optional)_
PodScrapeRelabelTemplate defines relabel config, that will be added to each VMPodScrape.
it's useful for adding specific labels to all targets | +| podScrapeSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | _(Optional)_
PodScrapeSelector defines PodScrapes to be selected for target discovery.
Works in combination with NamespaceSelector.
NamespaceSelector nil - only objects at VMAgent or VMSingle namespace.
Selector nil - only objects at NamespaceSelector namespaces.
If both nil - behaviour controlled by selectAllByDefault | | port#
_string_ | _(Optional)_
Port listen address | | priorityClassName#
_string_ | _(Optional)_
PriorityClassName class assigned to the Pods | +| probeNamespaceSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | _(Optional)_
ProbeNamespaceSelector defines Namespaces to be selected for VMProbe discovery.
Works in combination with Selector.
NamespaceSelector nil - only objects at VMAgent or VMSingle namespace.
Selector nil - only objects at NamespaceSelector namespaces.
If both nil - behaviour controlled by selectAllByDefault | +| 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.30/#labelselector-v1-meta)_ | _(Optional)_
ProbeSelector defines VMProbe to be selected for target probing.
Works in combination with NamespaceSelector.
NamespaceSelector nil - only objects at VMAgent or VMSingle 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.30/#podreadinessgate-v1-core) array_ | _(Required)_
ReadinessGates defines pod readiness gates | +| relabelConfig#
_[ConfigMapKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#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. | | 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.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 | | retentionPeriod#
_string_ | _(Optional)_
RetentionPeriod defines how long to retain stored metrics, specified as a duration (e.g., "1d", "1w", "1m").
Data with timestamps outside the RetentionPeriod is automatically deleted. The minimum allowed value is 1d, or 24h.
The default value is 1 (one month).
See [retention](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#retention) docs for details. | | 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/ | +| sampleLimit#
_integer_ | _(Optional)_
SampleLimit defines global per target limit of scraped samples | | schedulerName#
_string_ | _(Optional)_
SchedulerName - defines kubernetes scheduler name | +| scrapeClasses#
_[ScrapeClass](#scrapeclass) array_ | _(Optional)_
ScrapeClasses defines the list of scrape classes to expose to scraping objects such as
PodScrapes, ServiceScrapes, Probes and ScrapeConfigs. | +| scrapeConfigNamespaceSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | _(Optional)_
ScrapeConfigNamespaceSelector defines Namespaces to be selected for VMScrapeConfig discovery.
Works in combination with Selector.
NamespaceSelector nil - only objects at VMAgent or VMSingle namespace.
Selector nil - only objects at NamespaceSelector namespaces.
If both nil - behaviour controlled by selectAllByDefault | +| scrapeConfigRelabelTemplate#
_[RelabelConfig](#relabelconfig) array_ | _(Optional)_
ScrapeConfigRelabelTemplate defines relabel config, that will be added to each VMScrapeConfig.
it's useful for adding specific labels to all targets | +| scrapeConfigSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | _(Optional)_
ScrapeConfigSelector defines VMScrapeConfig to be selected for target discovery.
Works in combination with NamespaceSelector. | +| scrapeInterval#
_string_ | _(Optional)_
ScrapeInterval defines how often scrape targets by default | +| scrapeTimeout#
_string_ | _(Optional)_
ScrapeTimeout defines global timeout for targets scrape | | 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. | +| selectAllByDefault#
_boolean_ | _(Optional)_
SelectAllByDefault changes default behavior for empty CRD selectors, such ServiceScrapeSelector.
with selectAllByDefault: true and empty serviceScrapeSelector and ServiceScrapeNamespaceSelector
Operator selects all exist serviceScrapes
with selectAllByDefault: false - selects nothing | | serviceAccountName#
_string_ | _(Optional)_
ServiceAccountName is the name of the ServiceAccount to use to run the pods | +| serviceScrapeNamespaceSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | _(Optional)_
ServiceScrapeNamespaceSelector Namespaces to be selected for VMServiceScrape discovery.
Works in combination with Selector.
NamespaceSelector nil - only objects at VMAgent or VMSingle namespace.
Selector nil - only objects at NamespaceSelector namespaces.
If both nil - behaviour controlled by selectAllByDefault | +| serviceScrapeRelabelTemplate#
_[RelabelConfig](#relabelconfig) array_ | _(Optional)_
ServiceScrapeRelabelTemplate defines relabel config, that will be added to each VMServiceScrape.
it's useful for adding specific labels to all targets | +| serviceScrapeSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | _(Optional)_
ServiceScrapeSelector defines ServiceScrapes to be selected for target discovery.
Works in combination with NamespaceSelector.
NamespaceSelector nil - only objects at VMAgent or VMSingle namespace.
Selector nil - only objects at NamespaceSelector namespaces.
If both nil - behaviour controlled by selectAllByDefault | | 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 | +| staticScrapeNamespaceSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | _(Optional)_
StaticScrapeNamespaceSelector defines Namespaces to be selected for VMStaticScrape discovery.
Works in combination with NamespaceSelector.
NamespaceSelector nil - only objects at VMAgent or VMSingle 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 | +| staticScrapeSelector#
_[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#labelselector-v1-meta)_ | _(Optional)_
StaticScrapeSelector defines VMStaticScrape to be selected for target discovery.
Works in combination with NamespaceSelector.
If both nil - match everything.
NamespaceSelector nil - only objects at VMAgent or VMSingle namespace.
Selector nil - only objects at NamespaceSelector namespaces. | | storage#
_[PersistentVolumeClaimSpec](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#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 | @@ -4834,6 +4881,8 @@ Appears in: [VMSingle](#vmsingle) | topologySpreadConstraints#
_[TopologySpreadConstraint](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#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 | +| 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 | +| vmAgentExternalLabelName#
_string_ | _(Optional)_
VMAgentExternalLabelName Name of vmAgent external label used to denote vmAgent instance
name. Defaults to the value of `prometheus`. External label will
_not_ be added when value is set to empty string (`""`).
Deprecated: use externalLabelName instead. will be removed in v0.69.0 | | vmBackup#
_[VMBackup](#vmbackup)_ | _(Optional)_
VMBackup configuration for backup | | volumeMounts#
_[VolumeMount](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#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.30/#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/docs/resources/vmagent.md b/docs/resources/vmagent.md index 07fce71e5..af74c7449 100644 --- a/docs/resources/vmagent.md +++ b/docs/resources/vmagent.md @@ -193,7 +193,7 @@ metadata: spec: # ... selectAllByDefault: true - vmAgentExternalLabelName: vmagent_ha + externalLabelName: vmagent_ha remoteWrite: - url: "http://vmsingle-example.default.svc:8428/api/v1/write" scrapeInterval: 30s @@ -225,7 +225,7 @@ metadata: spec: # ... selectAllByDefault: true - vmAgentExternalLabelName: vmagent_ha + externalLabelName: vmagent_ha remoteWrite: - url: "http://vmsingle-example.default.svc:8428/api/v1/write" scrapeInterval: 30s @@ -259,7 +259,7 @@ metadata: spec: # ... selectAllByDefault: true - vmAgentExternalLabelName: vmagent_ha + externalLabelName: vmagent_ha remoteWrite: - url: "http://vmsingle-example.default.svc:8428/api/v1/write" # Replication: @@ -785,7 +785,7 @@ spec: scrapeTimeout: 10s externalLabels: cluster: my-cluster - vmAgentExternalLabelName: example + externalLabelName: example remoteWrite: - url: "http://vmsingle-example.default.svc:8428/api/v1/write" inlineRelabelConfig: diff --git a/internal/controller/operator/factory/build/build.go b/internal/controller/operator/factory/build/build.go index 1ab48df2a..ad5143de3 100644 --- a/internal/controller/operator/factory/build/build.go +++ b/internal/controller/operator/factory/build/build.go @@ -1,6 +1,8 @@ package build import ( + "bytes" + "compress/gzip" "fmt" "path" "strconv" @@ -251,3 +253,13 @@ func RelabelVolumeTo(volumes []corev1.Volume, mounts []corev1.VolumeMount, cr re } return volumes, mounts } + +// GzipConfig writes gzipped conf into buf +func GzipConfig(buf *bytes.Buffer, conf []byte) error { + w := gzip.NewWriter(buf) + defer w.Close() + if _, err := w.Write(conf); err != nil { + return err + } + return nil +} diff --git a/internal/controller/operator/factory/build/defaults.go b/internal/controller/operator/factory/build/defaults.go index 46e5df262..aa07ddb91 100644 --- a/internal/controller/operator/factory/build/defaults.go +++ b/internal/controller/operator/factory/build/defaults.go @@ -212,6 +212,9 @@ func addVMAgentDefaults(objI any) { cv := config.ApplicationDefaults(c.VMAgentDefault) addDefaultsToCommonParams(&cr.Spec.CommonDefaultableParams, cr.Spec.License, &cv) addDefaultsToConfigReloader(&cr.Spec.CommonConfigReloaderParams, ptr.Deref(cr.Spec.UseDefaultResources, false)) + if cr.Spec.IngestOnlyMode == nil { + cr.Spec.IngestOnlyMode = ptr.To(false) + } } func addVLAgentDefaults(objI any) { @@ -228,9 +231,13 @@ func addVMSingleDefaults(objI any) { useBackupDefaultResources := c.VMBackup.UseDefaultResources cv := config.ApplicationDefaults(c.VMSingleDefault) addDefaultsToCommonParams(&cr.Spec.CommonDefaultableParams, cr.Spec.License, &cv) + addDefaultsToConfigReloader(&cr.Spec.CommonConfigReloaderParams, ptr.Deref(cr.Spec.UseDefaultResources, false)) if cr.Spec.UseDefaultResources != nil { useBackupDefaultResources = *cr.Spec.UseDefaultResources } + if cr.Spec.IngestOnlyMode == nil { + cr.Spec.IngestOnlyMode = ptr.To(true) + } backupDefaults := &config.ApplicationDefaults{ Image: c.VMBackup.Image, Version: c.VMBackup.Version, diff --git a/internal/controller/operator/factory/finalize/vmsingle.go b/internal/controller/operator/factory/finalize/vmsingle.go index d7f9cb0b2..36a627300 100644 --- a/internal/controller/operator/factory/finalize/vmsingle.go +++ b/internal/controller/operator/factory/finalize/vmsingle.go @@ -5,9 +5,12 @@ import ( 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" "sigs.k8s.io/controller-runtime/pkg/client" vmv1beta1 "github.com/VictoriaMetrics/operator/api/operator/v1beta1" + "github.com/VictoriaMetrics/operator/internal/config" "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/build" ) @@ -31,9 +34,47 @@ func OnVMSingleDelete(ctx context.Context, rclient client.Client, cr *vmv1beta1. return err } } + if err := removeFinalizeObjByName(ctx, rclient, &corev1.ConfigMap{}, build.ResourceName(build.RelabelConfigResourceKind, cr), cr.Namespace); err != nil { + return err + } if err := removeFinalizeObjByName(ctx, rclient, &corev1.ConfigMap{}, build.ResourceName(build.StreamAggrConfigResourceKind, cr), cr.Namespace); err != nil { return err } + if config.IsClusterWideAccessAllowed() { + if err := removeFinalizeObjByName(ctx, rclient, &rbacv1.ClusterRoleBinding{}, cr.GetClusterRoleName(), cr.GetNamespace()); err != nil { + return err + } + if err := removeFinalizeObjByName(ctx, rclient, &rbacv1.ClusterRole{}, cr.GetClusterRoleName(), cr.GetNamespace()); err != nil { + return err + } + if err := SafeDelete(ctx, rclient, &rbacv1.ClusterRoleBinding{ObjectMeta: metav1.ObjectMeta{Name: cr.GetClusterRoleName(), Namespace: cr.GetNamespace()}}); err != nil { + return err + } + + if err := SafeDelete(ctx, rclient, &rbacv1.ClusterRole{ObjectMeta: metav1.ObjectMeta{Name: cr.GetClusterRoleName(), Namespace: cr.GetNamespace()}}); err != nil { + return err + } + } else { + if err := removeFinalizeObjByName(ctx, rclient, &rbacv1.RoleBinding{}, cr.GetClusterRoleName(), cr.GetNamespace()); err != nil { + return err + } + if err := removeFinalizeObjByName(ctx, rclient, &rbacv1.Role{}, cr.GetClusterRoleName(), cr.GetNamespace()); err != nil { + return err + } + if err := SafeDelete(ctx, rclient, &rbacv1.RoleBinding{ObjectMeta: metav1.ObjectMeta{Name: cr.GetClusterRoleName(), Namespace: cr.GetNamespace()}}); err != nil { + return err + } + + if err := SafeDelete(ctx, rclient, &rbacv1.Role{ObjectMeta: metav1.ObjectMeta{Name: cr.GetClusterRoleName(), Namespace: cr.GetNamespace()}}); err != nil { + return err + } + } + + if cr.Spec.AdditionalScrapeConfigs != nil { + if err := removeFinalizeObjByName(ctx, rclient, &corev1.Secret{}, cr.Spec.AdditionalScrapeConfigs.Name, cr.Namespace); err != nil { + return err + } + } if err := deleteSA(ctx, rclient, cr); err != nil { return err } diff --git a/internal/controller/operator/factory/vmagent/collect_scrapes.go b/internal/controller/operator/factory/vmagent/collect_scrapes.go deleted file mode 100644 index 51819e359..000000000 --- a/internal/controller/operator/factory/vmagent/collect_scrapes.go +++ /dev/null @@ -1,182 +0,0 @@ -package vmagent - -import ( - "context" - "fmt" - - "sigs.k8s.io/controller-runtime/pkg/client" - - vmv1beta1 "github.com/VictoriaMetrics/operator/api/operator/v1beta1" - "github.com/VictoriaMetrics/operator/internal/config" - "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/k8stools" - "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/logger" -) - -func selectScrapeConfigs(ctx context.Context, cr *vmv1beta1.VMAgent, rclient client.Client) ([]*vmv1beta1.VMScrapeConfig, []string, error) { - if cr.Spec.DaemonSetMode { - return nil, nil, nil - } - - var selectedConfigs []*vmv1beta1.VMScrapeConfig - var nsn []string - opts := &k8stools.SelectorOpts{ - SelectAll: cr.Spec.SelectAllByDefault, - NamespaceSelector: cr.Spec.ScrapeConfigNamespaceSelector, - ObjectSelector: cr.Spec.ScrapeConfigSelector, - DefaultNamespace: cr.Namespace, - } - if err := k8stools.VisitSelected(ctx, rclient, opts, func(list *vmv1beta1.VMScrapeConfigList) { - for i := range list.Items { - item := &list.Items[i] - if !item.DeletionTimestamp.IsZero() { - continue - } - selectedConfigs = append(selectedConfigs, item) - nsn = append(nsn, fmt.Sprintf("%s/%s", item.Namespace, item.Name)) - } - }); err != nil { - return nil, nil, err - } - return selectedConfigs, nsn, nil -} - -func selectPodScrapes(ctx context.Context, cr *vmv1beta1.VMAgent, rclient client.Client) ([]*vmv1beta1.VMPodScrape, []string, error) { - var selectedConfigs []*vmv1beta1.VMPodScrape - var nsn []string - opts := &k8stools.SelectorOpts{ - SelectAll: cr.Spec.SelectAllByDefault, - NamespaceSelector: cr.Spec.PodScrapeNamespaceSelector, - ObjectSelector: cr.Spec.PodScrapeSelector, - DefaultNamespace: cr.Namespace, - } - if err := k8stools.VisitSelected(ctx, rclient, opts, func(list *vmv1beta1.VMPodScrapeList) { - for i := range list.Items { - item := &list.Items[i] - if !item.DeletionTimestamp.IsZero() { - continue - } - selectedConfigs = append(selectedConfigs, item) - nsn = append(nsn, fmt.Sprintf("%s/%s", item.Namespace, item.Name)) - } - }); err != nil { - return nil, nil, err - } - return selectedConfigs, nsn, nil -} - -func selectProbes(ctx context.Context, cr *vmv1beta1.VMAgent, rclient client.Client) ([]*vmv1beta1.VMProbe, []string, error) { - if cr.Spec.DaemonSetMode { - return nil, nil, nil - } - var selectedConfigs []*vmv1beta1.VMProbe - var nsn []string - opts := &k8stools.SelectorOpts{ - SelectAll: cr.Spec.SelectAllByDefault, - NamespaceSelector: cr.Spec.ProbeNamespaceSelector, - ObjectSelector: cr.Spec.ProbeSelector, - DefaultNamespace: cr.Namespace, - } - if err := k8stools.VisitSelected(ctx, rclient, opts, func(list *vmv1beta1.VMProbeList) { - for i := range list.Items { - item := &list.Items[i] - if !item.DeletionTimestamp.IsZero() { - continue - } - selectedConfigs = append(selectedConfigs, item) - nsn = append(nsn, fmt.Sprintf("%s/%s", item.Namespace, item.Name)) - } - }); err != nil { - return nil, nil, err - } - return selectedConfigs, nsn, nil -} - -func selectNodeScrapes(ctx context.Context, cr *vmv1beta1.VMAgent, rclient client.Client) ([]*vmv1beta1.VMNodeScrape, []string, error) { - if cr.Spec.DaemonSetMode { - return nil, nil, nil - } - if !config.IsClusterWideAccessAllowed() && cr.IsOwnsServiceAccount() { - logger.WithContext(ctx).Info("cannot use VMNodeScrape at operator in single namespace mode with default permissions." + - " Create ServiceAccount for VMAgent manually if needed. Skipping config generation for it") - return nil, nil, nil - } - - var selectedConfigs []*vmv1beta1.VMNodeScrape - var nsn []string - opts := &k8stools.SelectorOpts{ - SelectAll: cr.Spec.SelectAllByDefault, - NamespaceSelector: cr.Spec.NodeScrapeNamespaceSelector, - ObjectSelector: cr.Spec.NodeScrapeSelector, - DefaultNamespace: cr.Namespace, - } - if err := k8stools.VisitSelected(ctx, rclient, opts, func(list *vmv1beta1.VMNodeScrapeList) { - for i := range list.Items { - item := &list.Items[i] - if !item.DeletionTimestamp.IsZero() { - continue - } - selectedConfigs = append(selectedConfigs, item) - nsn = append(nsn, fmt.Sprintf("%s/%s", item.Namespace, item.Name)) - - } - }); err != nil { - return nil, nil, err - } - return selectedConfigs, nsn, nil -} - -func selectStaticScrapes(ctx context.Context, cr *vmv1beta1.VMAgent, rclient client.Client) ([]*vmv1beta1.VMStaticScrape, []string, error) { - if cr.Spec.DaemonSetMode { - return nil, nil, nil - } - var selectedConfigs []*vmv1beta1.VMStaticScrape - var nsn []string - opts := &k8stools.SelectorOpts{ - SelectAll: cr.Spec.SelectAllByDefault, - NamespaceSelector: cr.Spec.StaticScrapeNamespaceSelector, - ObjectSelector: cr.Spec.StaticScrapeSelector, - DefaultNamespace: cr.Namespace, - } - if err := k8stools.VisitSelected(ctx, rclient, opts, func(list *vmv1beta1.VMStaticScrapeList) { - for i := range list.Items { - item := &list.Items[i] - if !item.DeletionTimestamp.IsZero() { - continue - } - selectedConfigs = append(selectedConfigs, item) - nsn = append(nsn, fmt.Sprintf("%s/%s", item.Namespace, item.Name)) - } - }); err != nil { - return nil, nil, err - } - return selectedConfigs, nsn, nil -} - -func selectServiceScrapes(ctx context.Context, cr *vmv1beta1.VMAgent, rclient client.Client) ([]*vmv1beta1.VMServiceScrape, []string, error) { - if cr.Spec.DaemonSetMode { - return nil, nil, nil - } - - var selectedConfigs []*vmv1beta1.VMServiceScrape - var nsn []string - opts := &k8stools.SelectorOpts{ - SelectAll: cr.Spec.SelectAllByDefault, - NamespaceSelector: cr.Spec.ServiceScrapeNamespaceSelector, - ObjectSelector: cr.Spec.ServiceScrapeSelector, - DefaultNamespace: cr.Namespace, - } - if err := k8stools.VisitSelected(ctx, rclient, opts, func(list *vmv1beta1.VMServiceScrapeList) { - for i := range list.Items { - item := &list.Items[i] - if !item.DeletionTimestamp.IsZero() { - continue - } - rclient.Scheme().Default(item) - nsn = append(nsn, fmt.Sprintf("%s/%s", item.Namespace, item.Name)) - selectedConfigs = append(selectedConfigs, item) - } - }); err != nil { - return nil, nil, err - } - return selectedConfigs, nsn, nil -} diff --git a/internal/controller/operator/factory/vmagent/vmagent_scrapeconfig_test.go b/internal/controller/operator/factory/vmagent/scrapes_test.go similarity index 96% rename from internal/controller/operator/factory/vmagent/vmagent_scrapeconfig_test.go rename to internal/controller/operator/factory/vmagent/scrapes_test.go index 3e45fd297..ff9994c2d 100644 --- a/internal/controller/operator/factory/vmagent/vmagent_scrapeconfig_test.go +++ b/internal/controller/operator/factory/vmagent/scrapes_test.go @@ -4,12 +4,10 @@ import ( "bytes" "compress/gzip" "context" - "encoding/json" "io" "testing" "github.com/stretchr/testify/assert" - "gopkg.in/yaml.v2" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -23,87 +21,6 @@ import ( "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/k8stools" ) -func Test_generateRelabelConfig(t *testing.T) { - f := func(rc *vmv1beta1.RelabelConfig, want string) { - // related fields only filled during json unmarshal - j, err := json.Marshal(rc) - if err != nil { - t.Fatalf("cannot serialize relabelConfig: %s", err) - } - var rlbCfg vmv1beta1.RelabelConfig - if err := json.Unmarshal(j, &rlbCfg); err != nil { - t.Fatalf("cannot parse relabelConfig: %s", err) - } - got := generateRelabelConfig(&rlbCfg) - gotBytes, err := yaml.Marshal(got) - if err != nil { - t.Errorf("cannot marshal generateRelabelConfig to yaml: %e", err) - return - } - assert.Equal(t, want, string(gotBytes)) - } - - // ok base cfg - f(&vmv1beta1.RelabelConfig{ - TargetLabel: "address", - SourceLabels: []string{"__address__"}, - Action: "replace", - }, `source_labels: -- __address__ -target_label: address -action: replace -`) - - // ok base with underscore - f(&vmv1beta1.RelabelConfig{ - UnderScoreTargetLabel: "address", - UnderScoreSourceLabels: []string{"__address__"}, - Action: "replace", - }, `source_labels: -- __address__ -target_label: address -action: replace -`) - - // ok base with graphite match labels - f(&vmv1beta1.RelabelConfig{ - UnderScoreTargetLabel: "address", - UnderScoreSourceLabels: []string{"__address__"}, - Action: "graphite", - Labels: map[string]string{"job": "$1", "instance": "${2}:8080"}, - Match: `foo.*.*.bar`, - }, `source_labels: -- __address__ -target_label: address -action: graphite -match: foo.*.*.bar -labels: - instance: ${2}:8080 - job: $1 -`) - - // with empty replacement and separator - f(&vmv1beta1.RelabelConfig{ - UnderScoreTargetLabel: "address", - UnderScoreSourceLabels: []string{"__address__"}, - Action: "graphite", - Labels: map[string]string{"job": "$1", "instance": "${2}:8080"}, - Match: `foo.*.*.bar`, - Separator: ptr.To(""), - Replacement: ptr.To(""), - }, `source_labels: -- __address__ -separator: "" -target_label: address -replacement: "" -action: graphite -match: foo.*.*.bar -labels: - instance: ${2}:8080 - job: $1 -`) -} - func TestCreateOrUpdateScrapeConfig(t *testing.T) { type opts struct { cr *vmv1beta1.VMAgent @@ -956,7 +873,6 @@ scrape_configs: UnderScoreSourceLabels: []string{"test2"}, }, }, - GlobalScrapeMetricRelabelConfigs: []*vmv1beta1.RelabelConfig{ { UnderScoreSourceLabels: []string{"test1"}, @@ -2240,7 +2156,7 @@ scrape_configs: [] } ac := getAssetsCache(ctx, testClient, cr) if err := createOrUpdateScrapeConfig(ctx, testClient, cr, nil, nil, ac); err != nil { - t.Errorf("CreateOrUpdateConfigurationSecret() error = %s", err) + t.Errorf("createOrUpdateScrapeConfig() error = %s", err) } var configSecret corev1.Secret if err := testClient.Get(ctx, types.NamespacedName{Namespace: cr.Namespace, Name: cr.PrefixedName()}, &configSecret); err != nil { diff --git a/internal/controller/operator/factory/vmagent/vmagent.go b/internal/controller/operator/factory/vmagent/vmagent.go index fb5534832..378551b33 100644 --- a/internal/controller/operator/factory/vmagent/vmagent.go +++ b/internal/controller/operator/factory/vmagent/vmagent.go @@ -1,6 +1,7 @@ package vmagent import ( + "bytes" "context" "fmt" "maps" @@ -27,25 +28,23 @@ import ( "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/k8stools" "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/logger" "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/reconcile" + "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/vmscrapes" ) 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" configFilename = "vmagent.yaml" defaultMaxDiskUsage = "1073741824" - - kubeNodeEnvName = "KUBE_NODE_NAME" - kubeNodeEnvTemplate = "%{" + kubeNodeEnvName + "}" ) func createOrUpdateService(ctx context.Context, rclient client.Client, cr, prevCR *vmv1beta1.VMAgent) (*corev1.Service, error) { @@ -97,6 +96,7 @@ func CreateOrUpdate(ctx context.Context, cr *vmv1beta1.VMAgent, rclient client.C return fmt.Errorf("cannot delete objects from prev state: %w", err) } } + ingestOnlyMode := ptr.Deref(cr.Spec.IngestOnlyMode, false) if cr.IsOwnsServiceAccount() { var prevSA *corev1.ServiceAccount if prevCR != nil { @@ -105,7 +105,7 @@ func CreateOrUpdate(ctx context.Context, cr *vmv1beta1.VMAgent, rclient client.C if err := reconcile.ServiceAccount(ctx, rclient, build.ServiceAccount(cr), prevSA); err != nil { return fmt.Errorf("failed create service account: %w", err) } - if !cr.Spec.IngestOnlyMode { + if !ingestOnlyMode { if err := createK8sAPIAccess(ctx, rclient, cr, prevCR, config.IsClusterWideAccessAllowed()); err != nil { return fmt.Errorf("cannot create vmagent role and binding for it, err: %w", err) } @@ -167,6 +167,7 @@ func createOrUpdateApp(ctx context.Context, rclient client.Client, cr, prevCR *v pdbToKeep := make(map[string]struct{}) shardCount := cr.GetShardCount() prevShardCount := prevCR.GetShardCount() + ingestOnlyMode := ptr.Deref(cr.Spec.IngestOnlyMode, false) isUpscaling := false if prevCR.IsSharded() { @@ -217,7 +218,7 @@ func createOrUpdateApp(ctx context.Context, rclient client.Client, cr, prevCR *v rv.err = fmt.Errorf("cannot fill placeholders for %T sharded vmagent(%d): %w", newAppTpl, shardNum, err) return } - if !cr.Spec.IngestOnlyMode { + if !ingestOnlyMode { patchShardContainers(newApp.Spec.Template.Spec.Containers, shardNum, shardCount) } } @@ -231,7 +232,7 @@ func createOrUpdateApp(ctx context.Context, rclient client.Client, cr, prevCR *v rv.err = fmt.Errorf("cannot fill placeholders for prev %T sharded vmagent(%d): %w", newAppTpl, shardNum, err) return } - if !prevCR.Spec.IngestOnlyMode { + if !ingestOnlyMode { patchShardContainers(prevApp.Spec.Template.Spec.Containers, shardNum, shardCount) } } else { @@ -255,7 +256,7 @@ func createOrUpdateApp(ctx context.Context, rclient client.Client, cr, prevCR *v rv.err = fmt.Errorf("cannot fill placeholders for %T in sharded vmagent(%d): %w", newAppTpl, shardNum, err) return } - if !cr.Spec.IngestOnlyMode { + if !ingestOnlyMode { patchShardContainers(newApp.Spec.Template.Spec.Containers, shardNum, shardCount) } } @@ -269,7 +270,7 @@ func createOrUpdateApp(ctx context.Context, rclient client.Client, cr, prevCR *v rv.err = fmt.Errorf("cannot fill placeholders for prev %T in sharded vmagent(%d): %w", newAppTpl, shardNum, err) return } - if !prevCR.Spec.IngestOnlyMode { + if !ingestOnlyMode { patchShardContainers(prevApp.Spec.Template.Spec.Containers, shardNum, shardCount) } } else { @@ -421,7 +422,7 @@ func newK8sApp(cr *vmv1beta1.VMAgent, ac *build.AssetsCache) (client.Object, err } 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) + cr.Spec.StatefulStorage.IntoSTSVolume(persistentQueueMountName, &stsSpec.Spec) stsSpec.Spec.VolumeClaimTemplates = append(stsSpec.Spec.VolumeClaimTemplates, cr.Spec.ClaimTemplates...) return stsSpec, nil } @@ -506,7 +507,7 @@ func newPodSpec(cr *vmv1beta1.VMAgent, ac *build.AssetsCache) (*corev1.PodSpec, if cr.Spec.DaemonSetMode { envs = append(envs, corev1.EnvVar{ - Name: kubeNodeEnvName, + Name: vmv1beta1.KubeNodeEnvName, ValueFrom: &corev1.EnvVarSource{ FieldRef: &corev1.ObjectFieldSelector{ FieldPath: "spec.nodeName", @@ -523,13 +524,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, }, ) @@ -539,7 +540,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{}, }, @@ -548,9 +549,10 @@ func newPodSpec(cr *vmv1beta1.VMAgent, ac *build.AssetsCache) (*corev1.PodSpec, volumes = append(volumes, cr.Spec.Volumes...) - if !cr.Spec.IngestOnlyMode { + ingestOnlyMode := ptr.Deref(cr.Spec.IngestOnlyMode, false) + if !ingestOnlyMode { 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 @@ -580,7 +582,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 @@ -592,7 +594,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,7 +677,7 @@ func newPodSpec(cr *vmv1beta1.VMAgent, ac *build.AssetsCache) (*corev1.PodSpec, var operatorContainers []corev1.Container var ic []corev1.Container // conditional add config reloader container - if !cr.Spec.IngestOnlyMode || cr.HasAnyRelabellingConfigs() || cr.HasAnyStreamAggrRule() { + if !ingestOnlyMode || cr.HasAnyRelabellingConfigs() || cr.HasAnyStreamAggrRule() { ss := &corev1.SecretKeySelector{ LocalObjectReference: corev1.LocalObjectReference{ Name: cr.PrefixedName(), @@ -684,7 +686,7 @@ func newPodSpec(cr *vmv1beta1.VMAgent, ac *build.AssetsCache) (*corev1.PodSpec, } configReloader := build.ConfigReloaderContainer(false, cr, crMounts, ss) operatorContainers = append(operatorContainers, configReloader) - if !cr.Spec.IngestOnlyMode { + if !ingestOnlyMode { ic = append(ic, build.ConfigReloaderContainer(true, cr, crMounts, ss)) build.AddStrictSecuritySettingsToContainers(cr.Spec.SecurityContext, ic, useStrictSecurity) } @@ -751,7 +753,7 @@ func buildRelabelingsAssets(cr *vmv1beta1.VMAgent, ac *build.AssetsCache) (*core } // global section if len(cr.Spec.InlineRelabelConfig) > 0 { - rcs := addRelabelConfigs(nil, cr.Spec.InlineRelabelConfig) + rcs := vmscrapes.AddRelabelConfigs(nil, cr.Spec.InlineRelabelConfig) data, err := yaml.Marshal(rcs) if err != nil { return nil, fmt.Errorf("cannot serialize relabelConfig as yaml: %w", err) @@ -774,7 +776,7 @@ func buildRelabelingsAssets(cr *vmv1beta1.VMAgent, ac *build.AssetsCache) (*core for i := range cr.Spec.RemoteWrite { rw := cr.Spec.RemoteWrite[i] if len(rw.InlineUrlRelabelConfig) > 0 { - rcs := addRelabelConfigs(nil, rw.InlineUrlRelabelConfig) + rcs := vmscrapes.AddRelabelConfigs(nil, rw.InlineUrlRelabelConfig) data, err := yaml.Marshal(rcs) if err != nil { return nil, fmt.Errorf("cannot serialize urlRelabelConfig as yaml: %w", err) @@ -911,9 +913,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() @@ -1211,3 +1213,129 @@ func removeStaleDaemonSet(ctx context.Context, rclient client.Client, cr *vmv1be owner := cr.AsOwner() return finalize.SafeDeleteWithFinalizer(ctx, rclient, &ds, &owner) } + +func getAssetsCache(ctx context.Context, rclient client.Client, cr *vmv1beta1.VMAgent) *build.AssetsCache { + cfg := map[build.ResourceKind]*build.ResourceCfg{ + build.SecretConfigResourceKind: { + MountDir: confDir, + SecretName: build.ResourceName(build.SecretConfigResourceKind, cr), + }, + build.TLSAssetsResourceKind: { + MountDir: tlsAssetsDir, + SecretName: build.ResourceName(build.TLSAssetsResourceKind, cr), + }, + } + return build.NewAssetsCache(ctx, rclient, cfg) +} + +// CreateOrUpdateScrapeConfig builds scrape configuration for VMAgent +func CreateOrUpdateScrapeConfig(ctx context.Context, rclient client.Client, cr *vmv1beta1.VMAgent, childObject client.Object) error { + var prevCR *vmv1beta1.VMAgent + if cr.ParsedLastAppliedSpec != nil { + prevCR = cr.DeepCopy() + prevCR.Spec = *cr.ParsedLastAppliedSpec + } + ac := getAssetsCache(ctx, rclient, cr) + if err := createOrUpdateScrapeConfig(ctx, rclient, cr, prevCR, childObject, ac); err != nil { + return err + } + return nil +} + +func createOrUpdateScrapeConfig(ctx context.Context, rclient client.Client, cr, prevCR *vmv1beta1.VMAgent, childObject client.Object, ac *build.AssetsCache) error { + ingestOnlyMode := ptr.Deref(cr.Spec.IngestOnlyMode, false) + if ingestOnlyMode { + return nil + } + // HACK: newPodSpec could load content into ac and it must be called + // before secret config reconcile + // + // TODO: @f41gh7 rewrite this section with VLAgent secret assets injection pattern + if _, err := newPodSpec(cr, ac); err != nil { + return err + } + + pos := &vmscrapes.ParsedObjects{ + Namespace: cr.Namespace, + APIServerConfig: cr.Spec.APIServerConfig, + MustUseNodeSelector: cr.Spec.DaemonSetMode, + HasClusterWideAccess: config.IsClusterWideAccessAllowed() || !cr.IsOwnsServiceAccount(), + ExternalLabels: buildExternalLabels(cr), + } + sp := &cr.Spec.CommonScrapeParams + if err := pos.Init(ctx, rclient, sp); err != nil { + return err + } + pos.ValidateObjects(sp) + + // Update secret based on the most recent configuration. + generatedConfig, err := pos.GenerateConfig( + ctx, + sp, + ac, + ) + if err != nil { + return fmt.Errorf("generating config for vmagent failed: %w", err) + } + + for kind, secret := range ac.GetOutput() { + var prevSecretMeta *metav1.ObjectMeta + if prevCR != nil { + prevSecretMeta = ptr.To(build.ResourceMeta(kind, prevCR)) + } + if kind == build.SecretConfigResourceKind { + // Compress config to avoid 1mb secret limit for a while + var buf bytes.Buffer + if err = build.GzipConfig(&buf, generatedConfig); err != nil { + return fmt.Errorf("cannot gzip config for vmagent: %w", err) + } + secret.Data[scrapeGzippedFilename] = buf.Bytes() + } + secret.ObjectMeta = build.ResourceMeta(kind, cr) + secret.Annotations = map[string]string{ + "generated": "true", + } + if err := reconcile.Secret(ctx, rclient, &secret, prevSecretMeta); err != nil { + return err + } + } + + parentName := fmt.Sprintf("%s.%s.vmagent", cr.Name, cr.Namespace) + if err := pos.UpdateStatusesForScrapeObjects(ctx, rclient, parentName, childObject); err != nil { + return err + } + + return nil +} + +func buildExternalLabels(cr *vmv1beta1.VMAgent) map[string]string { + m := map[string]string{} + sp := cr.Spec.CommonScrapeParams + + // Use "prometheus" external label name by default if field is missing. + // in case of migration from prometheus to vmagent, it helps to have same labels + // Do not add external label if field is set to empty string. + prometheusExternalLabelName := "prometheus" + var labelName *string + if sp.ExternalLabelName != nil { + labelName = sp.ExternalLabelName + } else if sp.VMAgentExternalLabelName != nil { + labelName = sp.VMAgentExternalLabelName + } + if labelName != nil { + if *labelName != "" { + prometheusExternalLabelName = *labelName + } else { + prometheusExternalLabelName = "" + } + } + + if prometheusExternalLabelName != "" { + m[prometheusExternalLabelName] = fmt.Sprintf("%s/%s", cr.Namespace, cr.Name) + } + + for n, v := range sp.ExternalLabels { + m[n] = v + } + return m +} diff --git a/internal/controller/operator/factory/vmagent/vmagent_test.go b/internal/controller/operator/factory/vmagent/vmagent_test.go index bc7962cdd..e5bf92045 100644 --- a/internal/controller/operator/factory/vmagent/vmagent_test.go +++ b/internal/controller/operator/factory/vmagent/vmagent_test.go @@ -154,7 +154,9 @@ func TestCreateOrUpdate(t *testing.T) { }, CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{}, StatefulMode: true, - IngestOnlyMode: true, + CommonScrapeParams: vmv1beta1.CommonScrapeParams{ + IngestOnlyMode: ptr.To(true), + }, StatefulStorage: &vmv1beta1.StorageSpec{ VolumeClaimTemplate: vmv1beta1.EmbeddedPersistentVolumeClaim{ Spec: corev1.PersistentVolumeClaimSpec{ @@ -544,7 +546,9 @@ func TestCreateOrUpdate(t *testing.T) { }, StatefulRollingUpdateStrategy: appsv1.RollingUpdateStatefulSetStrategyType, StatefulMode: true, - IngestOnlyMode: true, + CommonScrapeParams: vmv1beta1.CommonScrapeParams{ + IngestOnlyMode: ptr.To(true), + }, CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ ReplicaCount: ptr.To[int32](2), }, @@ -628,8 +632,10 @@ func TestCreateOrUpdate(t *testing.T) { CommonApplicationDeploymentParams: vmv1beta1.CommonApplicationDeploymentParams{ ReplicaCount: ptr.To(int32(1)), }, - StatefulMode: true, - IngestOnlyMode: true, + StatefulMode: true, + CommonScrapeParams: vmv1beta1.CommonScrapeParams{ + IngestOnlyMode: ptr.To(true), + }, StatefulStorage: &vmv1beta1.StorageSpec{ VolumeClaimTemplate: vmv1beta1.EmbeddedPersistentVolumeClaim{ Spec: corev1.PersistentVolumeClaimSpec{ @@ -1921,7 +1927,6 @@ func TestCreateOrUpdateRelabelConfigsAssets(t *testing.T) { Action: "DROP", SourceLabels: []string{"pod"}, }, - {}, }, }, }, @@ -2237,7 +2242,9 @@ func TestMakeSpecForAgentOk(t *testing.T) { cr: &vmv1beta1.VMAgent{ ObjectMeta: metav1.ObjectMeta{Name: "agent", Namespace: "default"}, Spec: vmv1beta1.VMAgentSpec{ - IngestOnlyMode: true, + CommonScrapeParams: vmv1beta1.CommonScrapeParams{ + IngestOnlyMode: ptr.To(true), + }, CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ Image: vmv1beta1.Image{ Repository: "vm-repo", @@ -2323,7 +2330,9 @@ serviceaccountname: vmagent-agent cr: &vmv1beta1.VMAgent{ ObjectMeta: metav1.ObjectMeta{Name: "agent", Namespace: "default"}, Spec: vmv1beta1.VMAgentSpec{ - IngestOnlyMode: false, + CommonScrapeParams: vmv1beta1.CommonScrapeParams{ + IngestOnlyMode: ptr.To(false), + }, CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ Image: vmv1beta1.Image{ Tag: "v1.97.1", @@ -2462,7 +2471,9 @@ serviceaccountname: vmagent-agent cr: &vmv1beta1.VMAgent{ ObjectMeta: metav1.ObjectMeta{Name: "agent", Namespace: "default"}, Spec: vmv1beta1.VMAgentSpec{ - IngestOnlyMode: true, + CommonScrapeParams: vmv1beta1.CommonScrapeParams{ + IngestOnlyMode: ptr.To(true), + }, CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ Image: vmv1beta1.Image{ Tag: "v1.97.1", @@ -2543,7 +2554,9 @@ serviceaccountname: vmagent-agent cr: &vmv1beta1.VMAgent{ ObjectMeta: metav1.ObjectMeta{Name: "agent", Namespace: "default"}, Spec: vmv1beta1.VMAgentSpec{ - IngestOnlyMode: true, + CommonScrapeParams: vmv1beta1.CommonScrapeParams{ + IngestOnlyMode: ptr.To(true), + }, CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ Image: vmv1beta1.Image{ Tag: "v1.97.1", @@ -2627,7 +2640,9 @@ serviceaccountname: vmagent-agent cr: &vmv1beta1.VMAgent{ ObjectMeta: metav1.ObjectMeta{Name: "agent", Namespace: "default"}, Spec: vmv1beta1.VMAgentSpec{ - IngestOnlyMode: true, + CommonScrapeParams: vmv1beta1.CommonScrapeParams{ + IngestOnlyMode: ptr.To(true), + }, CommonDefaultableParams: vmv1beta1.CommonDefaultableParams{ Image: vmv1beta1.Image{ Tag: "v1.97.1", diff --git a/internal/controller/operator/factory/vmalertmanager/statefulset.go b/internal/controller/operator/factory/vmalertmanager/statefulset.go index 2ec708707..552a98342 100644 --- a/internal/controller/operator/factory/vmalertmanager/statefulset.go +++ b/internal/controller/operator/factory/vmalertmanager/statefulset.go @@ -2,7 +2,6 @@ package vmalertmanager import ( "bytes" - "compress/gzip" "context" "fmt" "path" @@ -534,7 +533,7 @@ func CreateOrUpdateConfig(ctx context.Context, rclient client.Client, cr *vmv1be } var buf bytes.Buffer - if err := gzipConfig(&buf, alertmanagerConfig); err != nil { + if err := build.GzipConfig(&buf, alertmanagerConfig); err != nil { return fmt.Errorf("cannot gzip config for vmagent: %w", err) } newAMSecretConfig := &corev1.Secret{ @@ -607,15 +606,6 @@ func getSecretContentForAlertmanager(ctx context.Context, rclient client.Client, return nil, fmt.Errorf("cannot find alertmanager config key: %q at secret: %q", alertmanagerSecretConfigKey, secretName) } -func gzipConfig(buf *bytes.Buffer, conf []byte) error { - w := gzip.NewWriter(buf) - defer w.Close() - if _, err := w.Write(conf); err != nil { - return err - } - return nil -} - func buildAlertmanagerConfigWithCRDs(ctx context.Context, rclient client.Client, cr *vmv1beta1.VMAlertmanager, originConfig []byte, ac *build.AssetsCache) (*parsedObjects, []byte, error) { var configs []*vmv1beta1.VMAlertmanagerConfig var nsn []string diff --git a/internal/controller/operator/factory/vmauth/vmauth.go b/internal/controller/operator/factory/vmauth/vmauth.go index 764641709..61b59bcc5 100644 --- a/internal/controller/operator/factory/vmauth/vmauth.go +++ b/internal/controller/operator/factory/vmauth/vmauth.go @@ -2,7 +2,6 @@ package vmauth import ( "bytes" - "compress/gzip" "context" "fmt" "path" @@ -419,7 +418,7 @@ func CreateOrUpdateConfig(ctx context.Context, rclient client.Client, cr *vmv1be } var buf bytes.Buffer - if err := gzipConfig(&buf, generatedConfig); err != nil { + if err := build.GzipConfig(&buf, generatedConfig); err != nil { return fmt.Errorf("cannot gzip config for vmagent: %w", err) } s.Data[vmAuthConfigNameGz] = buf.Bytes() @@ -550,15 +549,6 @@ func buildIngressConfig(cr *vmv1beta1.VMAuth) *networkingv1.Ingress { } } -func gzipConfig(buf *bytes.Buffer, conf []byte) error { - w := gzip.NewWriter(buf) - defer w.Close() - if _, err := w.Write(conf); err != nil { - return err - } - return nil -} - func setInternalSvcPort(cr *vmv1beta1.VMAuth) func(svc *corev1.Service) { return func(svc *corev1.Service) { if len(cr.Spec.InternalListenPort) > 0 { diff --git a/internal/controller/operator/factory/vmagent/nodescrape.go b/internal/controller/operator/factory/vmscrapes/nodescrape.go similarity index 88% rename from internal/controller/operator/factory/vmagent/nodescrape.go rename to internal/controller/operator/factory/vmscrapes/nodescrape.go index 25fe9d7f2..816c4d60d 100644 --- a/internal/controller/operator/factory/vmagent/nodescrape.go +++ b/internal/controller/operator/factory/vmscrapes/nodescrape.go @@ -1,4 +1,4 @@ -package vmagent +package vmscrapes import ( "context" @@ -12,13 +12,13 @@ import ( func generateNodeScrapeConfig( ctx context.Context, - cr *vmv1beta1.VMAgent, + sp *vmv1beta1.CommonScrapeParams, + pos *ParsedObjects, sc *vmv1beta1.VMNodeScrape, ac *build.AssetsCache, ) (yaml.MapSlice, error) { spec := &sc.Spec - apiserverConfig := cr.Spec.APIServerConfig - se := cr.Spec.CommonScrapeSecurityEnforcements + se := &sp.CommonScrapeSecurityEnforcements cfg := yaml.MapSlice{ { Key: "job_name", @@ -26,18 +26,18 @@ func generateNodeScrapeConfig( }, } - scrapeClass := getScrapeClass(sc.Spec.ScrapeClassName, cr) + scrapeClass := getScrapeClass(sc.Spec.ScrapeClassName, sp) if scrapeClass != nil { mergeEndpointAuthWithScrapeClass(&sc.Spec.EndpointAuth, scrapeClass) mergeEndpointRelabelingsWithScrapeClass(&sc.Spec.EndpointRelabelings, scrapeClass) } - setScrapeIntervalToWithLimit(ctx, &spec.EndpointScrapeParams, cr) + setScrapeIntervalToWithLimit(ctx, &spec.EndpointScrapeParams, sp) k8sSDOpts := generateK8SSDConfigOptions{ - shouldAddSelectors: cr.Spec.EnableKubernetesAPISelectors, + shouldAddSelectors: sp.EnableKubernetesAPISelectors, selectors: sc.Spec.Selector, - apiServerConfig: apiserverConfig, + apiServerConfig: pos.APIServerConfig, role: k8sSDRoleNode, namespace: sc.Namespace, } @@ -51,7 +51,7 @@ func generateNodeScrapeConfig( var relabelings []yaml.MapSlice - skipRelabelSelectors := cr.Spec.EnableKubernetesAPISelectors + skipRelabelSelectors := sp.EnableKubernetesAPISelectors relabelings = addSelectorToRelabelingFor(relabelings, "node", spec.Selector, skipRelabelSelectors) // Add __address__ as internalIP and pod and service labels into proper labels. relabelings = append(relabelings, []yaml.MapSlice{ @@ -102,7 +102,7 @@ func generateNodeScrapeConfig( for _, c := range spec.RelabelConfigs { relabelings = append(relabelings, generateRelabelConfig(c)) } - for _, trc := range cr.Spec.NodeScrapeRelabelTemplate { + for _, trc := range sp.NodeScrapeRelabelTemplate { relabelings = append(relabelings, generateRelabelConfig(trc)) } diff --git a/internal/controller/operator/factory/vmagent/nodescrape_test.go b/internal/controller/operator/factory/vmscrapes/nodescrape_test.go similarity index 96% rename from internal/controller/operator/factory/vmagent/nodescrape_test.go rename to internal/controller/operator/factory/vmscrapes/nodescrape_test.go index 2f512f0c8..a9a97a92e 100644 --- a/internal/controller/operator/factory/vmagent/nodescrape_test.go +++ b/internal/controller/operator/factory/vmscrapes/nodescrape_test.go @@ -1,4 +1,4 @@ -package vmagent +package vmscrapes import ( "context" @@ -26,8 +26,10 @@ func Test_generateNodeScrapeConfig(t *testing.T) { t.Helper() ctx := context.Background() fclient := k8stools.GetTestClientWithObjects(o.predefinedObjects) - ac := getAssetsCache(ctx, fclient, o.cr) - got, err := generateNodeScrapeConfig(ctx, o.cr, o.sc, ac) + ac := getAssetsCache(ctx, fclient) + pos := &ParsedObjects{Namespace: o.cr.Namespace} + sp := &o.cr.Spec.CommonScrapeParams + got, err := generateNodeScrapeConfig(ctx, sp, pos, o.sc, ac) if err != nil { t.Errorf("cannot generate NodeScrapeConfig, err: %e", err) return diff --git a/internal/controller/operator/factory/vmscrapes/objects.go b/internal/controller/operator/factory/vmscrapes/objects.go new file mode 100644 index 000000000..57cb25b70 --- /dev/null +++ b/internal/controller/operator/factory/vmscrapes/objects.go @@ -0,0 +1,225 @@ +package vmscrapes + +import ( + "context" + "fmt" + + "sigs.k8s.io/controller-runtime/pkg/client" + + 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" + "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/logger" +) + +type ParsedObjects struct { + APIServerConfig *vmv1beta1.APIServerConfig + Namespace string + ExternalLabels map[string]string + MustUseNodeSelector bool + HasClusterWideAccess bool + serviceScrapes *build.ChildObjects[*vmv1beta1.VMServiceScrape] + podScrapes *build.ChildObjects[*vmv1beta1.VMPodScrape] + staticScrapes *build.ChildObjects[*vmv1beta1.VMStaticScrape] + nodeScrapes *build.ChildObjects[*vmv1beta1.VMNodeScrape] + probes *build.ChildObjects[*vmv1beta1.VMProbe] + scrapeConfigs *build.ChildObjects[*vmv1beta1.VMScrapeConfig] +} + +func (pos *ParsedObjects) updateMetrics(ctx context.Context) { + pos.serviceScrapes.UpdateMetrics(ctx) + pos.podScrapes.UpdateMetrics(ctx) + pos.staticScrapes.UpdateMetrics(ctx) + pos.nodeScrapes.UpdateMetrics(ctx) + pos.probes.UpdateMetrics(ctx) + pos.scrapeConfigs.UpdateMetrics(ctx) +} + +func (pos *ParsedObjects) Init(ctx context.Context, rclient client.Client, sp *vmv1beta1.CommonScrapeParams) error { + if err := pos.selectPodScrapes(ctx, rclient, sp); err != nil { + return fmt.Errorf("selecting PodScrapes failed: %w", err) + } + if err := pos.selectServiceScrapes(ctx, rclient, sp); err != nil { + return fmt.Errorf("selecting ServiceScrapes failed: %w", err) + } + if err := pos.selectProbes(ctx, rclient, sp); err != nil { + return fmt.Errorf("selecting VMProbes failed: %w", err) + } + if err := pos.selectNodeScrapes(ctx, rclient, sp); err != nil { + return fmt.Errorf("selecting VMNodeScrapes failed: %w", err) + } + if err := pos.selectStaticScrapes(ctx, rclient, sp); err != nil { + return fmt.Errorf("selecting VMStaticScrapes failed: %w", err) + } + if err := pos.selectScrapeConfigs(ctx, rclient, sp); err != nil { + return fmt.Errorf("selecting ScrapeConfigs failed: %w", err) + } + return nil +} + +func (pos *ParsedObjects) selectScrapeConfigs(ctx context.Context, rclient client.Client, sp *vmv1beta1.CommonScrapeParams) error { + var selectedConfigs []*vmv1beta1.VMScrapeConfig + var nsn []string + if !pos.MustUseNodeSelector { + opts := &k8stools.SelectorOpts{ + SelectAll: sp.SelectAllByDefault, + NamespaceSelector: sp.ScrapeConfigNamespaceSelector, + ObjectSelector: sp.ScrapeConfigSelector, + DefaultNamespace: pos.Namespace, + } + if err := k8stools.VisitSelected(ctx, rclient, opts, func(list *vmv1beta1.VMScrapeConfigList) { + for i := range list.Items { + item := &list.Items[i] + if !item.DeletionTimestamp.IsZero() { + continue + } + selectedConfigs = append(selectedConfigs, item) + nsn = append(nsn, fmt.Sprintf("%s/%s", item.Namespace, item.Name)) + } + }); err != nil { + return err + } + } + pos.scrapeConfigs = build.NewChildObjects("vmscrapeconfig", selectedConfigs, nsn) + return nil +} + +func (pos *ParsedObjects) selectPodScrapes(ctx context.Context, rclient client.Client, sp *vmv1beta1.CommonScrapeParams) error { + var selectedConfigs []*vmv1beta1.VMPodScrape + var nsn []string + opts := &k8stools.SelectorOpts{ + SelectAll: sp.SelectAllByDefault, + NamespaceSelector: sp.PodScrapeNamespaceSelector, + ObjectSelector: sp.PodScrapeSelector, + DefaultNamespace: pos.Namespace, + } + if err := k8stools.VisitSelected(ctx, rclient, opts, func(list *vmv1beta1.VMPodScrapeList) { + for i := range list.Items { + item := &list.Items[i] + if !item.DeletionTimestamp.IsZero() { + continue + } + selectedConfigs = append(selectedConfigs, item) + nsn = append(nsn, fmt.Sprintf("%s/%s", item.Namespace, item.Name)) + } + }); err != nil { + return err + } + pos.podScrapes = build.NewChildObjects("vmpodscrape", selectedConfigs, nsn) + return nil +} + +func (pos *ParsedObjects) selectProbes(ctx context.Context, rclient client.Client, sp *vmv1beta1.CommonScrapeParams) error { + var selectedConfigs []*vmv1beta1.VMProbe + var nsn []string + if !pos.MustUseNodeSelector { + opts := &k8stools.SelectorOpts{ + SelectAll: sp.SelectAllByDefault, + NamespaceSelector: sp.ProbeNamespaceSelector, + ObjectSelector: sp.ProbeSelector, + DefaultNamespace: pos.Namespace, + } + if err := k8stools.VisitSelected(ctx, rclient, opts, func(list *vmv1beta1.VMProbeList) { + for i := range list.Items { + item := &list.Items[i] + if !item.DeletionTimestamp.IsZero() { + continue + } + selectedConfigs = append(selectedConfigs, item) + nsn = append(nsn, fmt.Sprintf("%s/%s", item.Namespace, item.Name)) + } + }); err != nil { + return err + } + } + pos.probes = build.NewChildObjects("vmprobe", selectedConfigs, nsn) + return nil +} + +func (pos *ParsedObjects) selectNodeScrapes(ctx context.Context, rclient client.Client, sp *vmv1beta1.CommonScrapeParams) error { + var selectedConfigs []*vmv1beta1.VMNodeScrape + var nsn []string + if !pos.MustUseNodeSelector { + if pos.HasClusterWideAccess { + opts := &k8stools.SelectorOpts{ + SelectAll: sp.SelectAllByDefault, + NamespaceSelector: sp.NodeScrapeNamespaceSelector, + ObjectSelector: sp.NodeScrapeSelector, + DefaultNamespace: pos.Namespace, + } + if err := k8stools.VisitSelected(ctx, rclient, opts, func(list *vmv1beta1.VMNodeScrapeList) { + for i := range list.Items { + item := &list.Items[i] + if !item.DeletionTimestamp.IsZero() { + continue + } + selectedConfigs = append(selectedConfigs, item) + nsn = append(nsn, fmt.Sprintf("%s/%s", item.Namespace, item.Name)) + + } + }); err != nil { + return err + } + } else { + logger.WithContext(ctx).Info("cannot use VMNodeScrape at operator in single namespace mode with default permissions." + + " Create ServiceAccount manually if needed. Skipping config generation for it") + } + } + pos.nodeScrapes = build.NewChildObjects("vmnodescrape", selectedConfigs, nsn) + return nil +} + +func (pos *ParsedObjects) selectStaticScrapes(ctx context.Context, rclient client.Client, sp *vmv1beta1.CommonScrapeParams) error { + var selectedConfigs []*vmv1beta1.VMStaticScrape + var nsn []string + if !pos.MustUseNodeSelector { + opts := &k8stools.SelectorOpts{ + SelectAll: sp.SelectAllByDefault, + NamespaceSelector: sp.StaticScrapeNamespaceSelector, + ObjectSelector: sp.StaticScrapeSelector, + DefaultNamespace: pos.Namespace, + } + if err := k8stools.VisitSelected(ctx, rclient, opts, func(list *vmv1beta1.VMStaticScrapeList) { + for i := range list.Items { + item := &list.Items[i] + if !item.DeletionTimestamp.IsZero() { + continue + } + selectedConfigs = append(selectedConfigs, item) + nsn = append(nsn, fmt.Sprintf("%s/%s", item.Namespace, item.Name)) + } + }); err != nil { + return err + } + } + pos.staticScrapes = build.NewChildObjects("vmstaticscrape", selectedConfigs, nsn) + return nil +} + +func (pos *ParsedObjects) selectServiceScrapes(ctx context.Context, rclient client.Client, sp *vmv1beta1.CommonScrapeParams) error { + var selectedConfigs []*vmv1beta1.VMServiceScrape + var nsn []string + if !pos.MustUseNodeSelector { + opts := &k8stools.SelectorOpts{ + SelectAll: sp.SelectAllByDefault, + NamespaceSelector: sp.ServiceScrapeNamespaceSelector, + ObjectSelector: sp.ServiceScrapeSelector, + DefaultNamespace: pos.Namespace, + } + if err := k8stools.VisitSelected(ctx, rclient, opts, func(list *vmv1beta1.VMServiceScrapeList) { + for i := range list.Items { + item := &list.Items[i] + if !item.DeletionTimestamp.IsZero() { + continue + } + rclient.Scheme().Default(item) + nsn = append(nsn, fmt.Sprintf("%s/%s", item.Namespace, item.Name)) + selectedConfigs = append(selectedConfigs, item) + } + }); err != nil { + return err + } + } + pos.serviceScrapes = build.NewChildObjects("vmservicescrape", selectedConfigs, nsn) + return nil +} diff --git a/internal/controller/operator/factory/vmagent/collect_scrapes_test.go b/internal/controller/operator/factory/vmscrapes/objects_test.go similarity index 94% rename from internal/controller/operator/factory/vmagent/collect_scrapes_test.go rename to internal/controller/operator/factory/vmscrapes/objects_test.go index 2ba32df18..eeb9bf664 100644 --- a/internal/controller/operator/factory/vmagent/collect_scrapes_test.go +++ b/internal/controller/operator/factory/vmscrapes/objects_test.go @@ -1,4 +1,4 @@ -package vmagent +package vmscrapes import ( "context" @@ -8,6 +8,7 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -55,13 +56,11 @@ func TestSelectServiceMonitors(t *testing.T) { f := func(o opts) { t.Helper() fclient := k8stools.GetTestClientWithObjects(o.predefinedObjects) - got, _, err := selectServiceScrapes(context.TODO(), o.cr, fclient) - if err != nil { - t.Errorf("SelectServiceScrapes() error = %v", err) - return - } + sp := &o.cr.Spec.CommonScrapeParams + pos := &ParsedObjects{Namespace: o.cr.Namespace} + assert.NoError(t, pos.selectServiceScrapes(context.TODO(), fclient, sp)) gotNames := []string{} - for _, monitorName := range got { + for _, monitorName := range pos.serviceScrapes.All() { gotNames = append(gotNames, fmt.Sprintf("%s/%s", monitorName.Namespace, monitorName.Name)) } sort.Strings(gotNames) @@ -287,14 +286,12 @@ func TestSelectPodMonitors(t *testing.T) { } f := func(o opts) { fclient := k8stools.GetTestClientWithObjects(o.predefinedObjects) - got, _, err := selectPodScrapes(context.TODO(), o.cr, fclient) - if err != nil { - t.Errorf("SelectPodScrapes() error = %v", err) - return - } + sp := &o.cr.Spec.CommonScrapeParams + pos := &ParsedObjects{Namespace: o.cr.Namespace} + assert.NoError(t, pos.selectPodScrapes(context.TODO(), fclient, sp)) var gotNames []string - for _, k := range got { + for _, k := range pos.podScrapes.All() { gotNames = append(gotNames, fmt.Sprintf("%s/%s", k.Namespace, k.Name)) } sort.Strings(gotNames) @@ -375,18 +372,16 @@ func TestSelectProbes(t *testing.T) { f := func(o opts) { t.Helper() fclient := k8stools.GetTestClientWithObjects(o.predefinedObjects) - got, _, err := selectProbes(context.TODO(), o.cr, fclient) - if err != nil { - t.Errorf("SelectProbes() error = %v", err) - return - } + sp := &o.cr.Spec.CommonScrapeParams + pos := &ParsedObjects{Namespace: o.cr.Namespace} + assert.NoError(t, pos.selectProbes(context.TODO(), fclient, sp)) var result []string - for _, k := range got { + for _, k := range pos.probes.All() { result = append(result, fmt.Sprintf("%s/%s", k.Namespace, k.Name)) } sort.Strings(result) if !reflect.DeepEqual(result, o.want) { - t.Errorf("SelectProbes(): %s", cmp.Diff(got, o.want)) + t.Errorf("SelectProbes(): %s", cmp.Diff(result, o.want)) } } diff --git a/internal/controller/operator/factory/vmagent/podscrape.go b/internal/controller/operator/factory/vmscrapes/podscrape.go similarity index 89% rename from internal/controller/operator/factory/vmagent/podscrape.go rename to internal/controller/operator/factory/vmscrapes/podscrape.go index beaa520a9..6c40470a8 100644 --- a/internal/controller/operator/factory/vmagent/podscrape.go +++ b/internal/controller/operator/factory/vmscrapes/podscrape.go @@ -1,4 +1,4 @@ -package vmagent +package vmscrapes import ( "context" @@ -12,15 +12,15 @@ import ( func generatePodScrapeConfig( ctx context.Context, - cr *vmv1beta1.VMAgent, + sp *vmv1beta1.CommonScrapeParams, + pos *ParsedObjects, sc *vmv1beta1.VMPodScrape, ep vmv1beta1.PodMetricsEndpoint, i int, ac *build.AssetsCache, ) (yaml.MapSlice, error) { spec := &sc.Spec - apiserverConfig := cr.Spec.APIServerConfig - se := cr.Spec.CommonScrapeSecurityEnforcements + se := &sp.CommonScrapeSecurityEnforcements cfg := yaml.MapSlice{ { Key: "job_name", @@ -28,7 +28,7 @@ func generatePodScrapeConfig( }, } - scrapeClass := getScrapeClass(spec.ScrapeClassName, cr) + scrapeClass := getScrapeClass(spec.ScrapeClassName, sp) if scrapeClass != nil { mergeEndpointAuthWithScrapeClass(&ep.EndpointAuth, scrapeClass) mergeEndpointRelabelingsWithScrapeClass(&ep.EndpointRelabelings, scrapeClass) @@ -44,16 +44,14 @@ func generatePodScrapeConfig( } k8sSDOpts := generateK8SSDConfigOptions{ - namespaces: selectedNamespaces, - shouldAddSelectors: cr.Spec.EnableKubernetesAPISelectors, - selectors: spec.Selector, - apiServerConfig: apiserverConfig, - role: k8sSDRolePod, - attachMetadata: &ep.AttachMetadata, - namespace: sc.Namespace, - } - if cr.Spec.DaemonSetMode { - k8sSDOpts.mustUseNodeSelector = true + namespaces: selectedNamespaces, + shouldAddSelectors: sp.EnableKubernetesAPISelectors, + selectors: spec.Selector, + apiServerConfig: pos.APIServerConfig, + role: k8sSDRolePod, + attachMetadata: &ep.AttachMetadata, + namespace: sc.Namespace, + mustUseNodeSelector: pos.MustUseNodeSelector, } if c, err := generateK8SSDConfig(ac, k8sSDOpts); err != nil { return nil, err @@ -69,7 +67,7 @@ func generatePodScrapeConfig( ep.SeriesLimit = spec.SeriesLimit } - setScrapeIntervalToWithLimit(ctx, &ep.EndpointScrapeParams, cr) + setScrapeIntervalToWithLimit(ctx, &ep.EndpointScrapeParams, sp) cfg = addCommonScrapeParamsTo(cfg, ep.EndpointScrapeParams, se) @@ -83,7 +81,7 @@ func generatePodScrapeConfig( }) } - skipRelabelSelectors := cr.Spec.EnableKubernetesAPISelectors + skipRelabelSelectors := sp.EnableKubernetesAPISelectors relabelings = addSelectorToRelabelingFor(relabelings, "pod", spec.Selector, skipRelabelSelectors) // Filter targets based on correct port for the endpoint. @@ -177,7 +175,7 @@ func generatePodScrapeConfig( for _, c := range ep.RelabelConfigs { relabelings = append(relabelings, generateRelabelConfig(c)) } - for _, trc := range cr.Spec.PodScrapeRelabelTemplate { + for _, trc := range sp.PodScrapeRelabelTemplate { relabelings = append(relabelings, generateRelabelConfig(trc)) } // Because of security risks, whenever enforcedNamespaceLabel is set, we want to append it to the diff --git a/internal/controller/operator/factory/vmagent/podscrape_test.go b/internal/controller/operator/factory/vmscrapes/podscrape_test.go similarity index 97% rename from internal/controller/operator/factory/vmagent/podscrape_test.go rename to internal/controller/operator/factory/vmscrapes/podscrape_test.go index 17f109b82..78cff1c34 100644 --- a/internal/controller/operator/factory/vmagent/podscrape_test.go +++ b/internal/controller/operator/factory/vmscrapes/podscrape_test.go @@ -1,4 +1,4 @@ -package vmagent +package vmscrapes import ( "context" @@ -25,8 +25,10 @@ func Test_generatePodScrapeConfig(t *testing.T) { t.Helper() ctx := context.Background() fclient := k8stools.GetTestClientWithObjects(nil) - ac := getAssetsCache(ctx, fclient, o.cr) - got, err := generatePodScrapeConfig(ctx, o.cr, o.sc, o.ep, 0, ac) + ac := getAssetsCache(ctx, fclient) + pos := &ParsedObjects{Namespace: o.cr.Namespace} + sp := &o.cr.Spec.CommonScrapeParams + got, err := generatePodScrapeConfig(ctx, sp, pos, o.sc, o.ep, 0, ac) if err != nil { t.Errorf("cannot generate PodScrapeConfig, err: %e", err) return diff --git a/internal/controller/operator/factory/vmagent/probe.go b/internal/controller/operator/factory/vmscrapes/probe.go similarity index 90% rename from internal/controller/operator/factory/vmagent/probe.go rename to internal/controller/operator/factory/vmscrapes/probe.go index 6cbbf3267..b5a2ba491 100644 --- a/internal/controller/operator/factory/vmagent/probe.go +++ b/internal/controller/operator/factory/vmscrapes/probe.go @@ -1,4 +1,4 @@ -package vmagent +package vmscrapes import ( "context" @@ -12,13 +12,13 @@ import ( func generateProbeConfig( ctx context.Context, - cr *vmv1beta1.VMAgent, + sp *vmv1beta1.CommonScrapeParams, + pos *ParsedObjects, sc *vmv1beta1.VMProbe, ac *build.AssetsCache, ) (yaml.MapSlice, error) { spec := &sc.Spec - apiserverConfig := cr.Spec.APIServerConfig - se := cr.Spec.CommonScrapeSecurityEnforcements + se := &sp.CommonScrapeSecurityEnforcements cfg := yaml.MapSlice{ { Key: "job_name", @@ -26,7 +26,7 @@ func generateProbeConfig( }, } - scrapeClass := getScrapeClass(spec.ScrapeClassName, cr) + scrapeClass := getScrapeClass(spec.ScrapeClassName, sp) if scrapeClass != nil { mergeEndpointAuthWithScrapeClass(&spec.EndpointAuth, scrapeClass) } @@ -47,7 +47,7 @@ func generateProbeConfig( spec.Scheme = spec.VMProberSpec.Scheme } - setScrapeIntervalToWithLimit(ctx, &spec.EndpointScrapeParams, cr) + setScrapeIntervalToWithLimit(ctx, &spec.EndpointScrapeParams, sp) cfg = addCommonScrapeParamsTo(cfg, spec.EndpointScrapeParams, se) @@ -80,15 +80,15 @@ func generateProbeConfig( } if spec.Targets.Ingress != nil { - skipRelabelSelectors := cr.Spec.EnableKubernetesAPISelectors + skipRelabelSelectors := sp.EnableKubernetesAPISelectors relabelings = addSelectorToRelabelingFor(relabelings, "ingress", spec.Targets.Ingress.Selector, skipRelabelSelectors) selectedNamespaces := getNamespacesFromNamespaceSelector(&spec.Targets.Ingress.NamespaceSelector, sc.Namespace, se.IgnoreNamespaceSelectors) k8sSDOpts := generateK8SSDConfigOptions{ namespaces: selectedNamespaces, - shouldAddSelectors: cr.Spec.EnableKubernetesAPISelectors, + shouldAddSelectors: sp.EnableKubernetesAPISelectors, selectors: spec.Targets.Ingress.Selector, - apiServerConfig: apiserverConfig, + apiServerConfig: pos.APIServerConfig, role: k8sSDRoleIngress, namespace: sc.Namespace, } @@ -152,7 +152,7 @@ func generateProbeConfig( }, }...) - for _, trc := range cr.Spec.ProbeScrapeRelabelTemplate { + for _, trc := range sp.ProbeScrapeRelabelTemplate { relabelings = append(relabelings, generateRelabelConfig(trc)) } // Because of security risks, whenever enforcedNamespaceLabel is set, we want to append it to the diff --git a/internal/controller/operator/factory/vmagent/probe_test.go b/internal/controller/operator/factory/vmscrapes/probe_test.go similarity index 96% rename from internal/controller/operator/factory/vmagent/probe_test.go rename to internal/controller/operator/factory/vmscrapes/probe_test.go index 784750e35..c598c22f6 100644 --- a/internal/controller/operator/factory/vmagent/probe_test.go +++ b/internal/controller/operator/factory/vmscrapes/probe_test.go @@ -1,4 +1,4 @@ -package vmagent +package vmscrapes import ( "context" @@ -27,8 +27,10 @@ func Test_generateProbeConfig(t *testing.T) { t.Helper() ctx := context.Background() fclient := k8stools.GetTestClientWithObjects(o.predefinedObjects) - ac := getAssetsCache(ctx, fclient, o.cr) - got, err := generateProbeConfig(ctx, o.cr, o.sc, ac) + ac := getAssetsCache(ctx, fclient) + pos := &ParsedObjects{Namespace: o.cr.Namespace} + sp := &o.cr.Spec.CommonScrapeParams + got, err := generateProbeConfig(ctx, sp, pos, o.sc, ac) if err != nil { t.Errorf("cannot generate ProbeConfig, err: %e", err) return @@ -261,8 +263,8 @@ relabel_configs: replacement: blackbox-monitor:9115 stream_parse: false proxy_tls_config: - ca_file: /etc/vmagent-tls/certs/default_configmap_tls-secret_ca - cert_file: /etc/vmagent-tls/certs/default_tls-secret_cert + ca_file: /tls/default_configmap_tls-secret_ca + cert_file: /tls/default_tls-secret_cert key_file: /tmp/key-1 bearer_token_file: /tmp/some_path basic_auth: diff --git a/internal/controller/operator/factory/vmagent/scrapeconfig.go b/internal/controller/operator/factory/vmscrapes/scrapeconfig.go similarity index 98% rename from internal/controller/operator/factory/vmagent/scrapeconfig.go rename to internal/controller/operator/factory/vmscrapes/scrapeconfig.go index 79c7d5f7f..c81ec6abc 100644 --- a/internal/controller/operator/factory/vmagent/scrapeconfig.go +++ b/internal/controller/operator/factory/vmscrapes/scrapeconfig.go @@ -1,4 +1,4 @@ -package vmagent +package vmscrapes import ( "context" @@ -13,12 +13,12 @@ import ( func generateScrapeConfig( ctx context.Context, - cr *vmv1beta1.VMAgent, + sp *vmv1beta1.CommonScrapeParams, sc *vmv1beta1.VMScrapeConfig, ac *build.AssetsCache, ) (yaml.MapSlice, error) { spec := &sc.Spec - se := cr.Spec.CommonScrapeSecurityEnforcements + se := &sp.CommonScrapeSecurityEnforcements jobName := fmt.Sprintf("scrapeConfig/%s/%s", sc.Namespace, sc.Name) cfg := yaml.MapSlice{ { @@ -27,13 +27,13 @@ func generateScrapeConfig( }, } - scrapeClass := getScrapeClass(spec.ScrapeClassName, cr) + scrapeClass := getScrapeClass(spec.ScrapeClassName, sp) if scrapeClass != nil { mergeEndpointAuthWithScrapeClass(&spec.EndpointAuth, scrapeClass) mergeEndpointRelabelingsWithScrapeClass(&spec.EndpointRelabelings, scrapeClass) } - setScrapeIntervalToWithLimit(ctx, &spec.EndpointScrapeParams, cr) + setScrapeIntervalToWithLimit(ctx, &spec.EndpointScrapeParams, sp) cfg = addCommonScrapeParamsTo(cfg, spec.EndpointScrapeParams, se) @@ -41,7 +41,7 @@ func generateScrapeConfig( for _, c := range spec.RelabelConfigs { relabelings = append(relabelings, generateRelabelConfig(c)) } - for _, trc := range cr.Spec.ScrapeConfigRelabelTemplate { + for _, trc := range sp.ScrapeConfigRelabelTemplate { relabelings = append(relabelings, generateRelabelConfig(trc)) } // Because of security risks, whenever enforcedNamespaceLabel is set, we want to append it to the diff --git a/internal/controller/operator/factory/vmagent/scrapeconfig_test.go b/internal/controller/operator/factory/vmscrapes/scrapeconfig_test.go similarity index 98% rename from internal/controller/operator/factory/vmagent/scrapeconfig_test.go rename to internal/controller/operator/factory/vmscrapes/scrapeconfig_test.go index 55c0ec5db..67a4690b3 100644 --- a/internal/controller/operator/factory/vmagent/scrapeconfig_test.go +++ b/internal/controller/operator/factory/vmscrapes/scrapeconfig_test.go @@ -1,4 +1,4 @@ -package vmagent +package vmscrapes import ( "context" @@ -28,8 +28,9 @@ func TestGenerateScrapeConfig(t *testing.T) { t.Helper() ctx := context.Background() fclient := k8stools.GetTestClientWithObjects(o.predefinedObjects) - ac := getAssetsCache(ctx, fclient, o.cr) - got, err := generateScrapeConfig(ctx, o.cr, o.sc, ac) + ac := getAssetsCache(ctx, fclient) + sp := &o.cr.Spec.CommonScrapeParams + got, err := generateScrapeConfig(ctx, sp, o.sc, ac) if err != nil { t.Errorf("cannot execute generateScrapeConfig, err: %e", err) return @@ -270,8 +271,8 @@ http_sd_configs: credentials: auth-secret type: Bearer tls_config: - ca_file: /etc/vmagent-tls/certs/default_tls-secret_ca - cert_file: /etc/vmagent-tls/certs/default_tls-secret_cert + ca_file: /tls/default_tls-secret_ca + cert_file: /tls/default_tls-secret_cert `, }) diff --git a/internal/controller/operator/factory/vmagent/servicescrape.go b/internal/controller/operator/factory/vmscrapes/servicescrape.go similarity index 95% rename from internal/controller/operator/factory/vmagent/servicescrape.go rename to internal/controller/operator/factory/vmscrapes/servicescrape.go index 8a86a9bb3..fc8f797e1 100644 --- a/internal/controller/operator/factory/vmagent/servicescrape.go +++ b/internal/controller/operator/factory/vmscrapes/servicescrape.go @@ -1,4 +1,4 @@ -package vmagent +package vmscrapes import ( "context" @@ -12,16 +12,16 @@ import ( func generateServiceScrapeConfig( ctx context.Context, - cr *vmv1beta1.VMAgent, + sp *vmv1beta1.CommonScrapeParams, + pos *ParsedObjects, sc *vmv1beta1.VMServiceScrape, ep vmv1beta1.Endpoint, i int, ac *build.AssetsCache, ) (yaml.MapSlice, error) { spec := &sc.Spec - apiserverConfig := cr.Spec.APIServerConfig - se := cr.Spec.CommonScrapeSecurityEnforcements - scrapeClass := getScrapeClass(spec.ScrapeClassName, cr) + se := &sp.CommonScrapeSecurityEnforcements + scrapeClass := getScrapeClass(spec.ScrapeClassName, sp) if scrapeClass != nil { mergeEndpointAuthWithScrapeClass(&ep.EndpointAuth, scrapeClass) mergeEndpointRelabelingsWithScrapeClass(&ep.EndpointRelabelings, scrapeClass) @@ -48,9 +48,9 @@ func generateServiceScrapeConfig( } k8sSDOpts := generateK8SSDConfigOptions{ namespaces: selectedNamespaces, - shouldAddSelectors: cr.Spec.EnableKubernetesAPISelectors, + shouldAddSelectors: sp.EnableKubernetesAPISelectors, selectors: spec.Selector, - apiServerConfig: apiserverConfig, + apiServerConfig: pos.APIServerConfig, role: spec.DiscoveryRole, attachMetadata: &ep.AttachMetadata, namespace: sc.Namespace, @@ -68,14 +68,14 @@ func generateServiceScrapeConfig( ep.SeriesLimit = spec.SeriesLimit } - setScrapeIntervalToWithLimit(ctx, &ep.EndpointScrapeParams, cr) + setScrapeIntervalToWithLimit(ctx, &ep.EndpointScrapeParams, sp) cfg = addCommonScrapeParamsTo(cfg, ep.EndpointScrapeParams, se) var relabelings []yaml.MapSlice // Exact label matches. - skipRelabelSelectors := cr.Spec.EnableKubernetesAPISelectors + skipRelabelSelectors := sp.EnableKubernetesAPISelectors relabelings = addSelectorToRelabelingFor(relabelings, "service", spec.Selector, skipRelabelSelectors) // Filter targets based on correct port for the endpoint. @@ -243,7 +243,7 @@ func generateServiceScrapeConfig( relabelings = append(relabelings, generateRelabelConfig(c)) } - for _, trc := range cr.Spec.ServiceScrapeRelabelTemplate { + for _, trc := range sp.ServiceScrapeRelabelTemplate { relabelings = append(relabelings, generateRelabelConfig(trc)) } diff --git a/internal/controller/operator/factory/vmagent/servicescrape_test.go b/internal/controller/operator/factory/vmscrapes/servicescrape_test.go similarity index 98% rename from internal/controller/operator/factory/vmagent/servicescrape_test.go rename to internal/controller/operator/factory/vmscrapes/servicescrape_test.go index 5bb512aed..bd7e9aeab 100644 --- a/internal/controller/operator/factory/vmagent/servicescrape_test.go +++ b/internal/controller/operator/factory/vmscrapes/servicescrape_test.go @@ -1,4 +1,4 @@ -package vmagent +package vmscrapes import ( "context" @@ -28,8 +28,13 @@ func Test_generateServiceScrapeConfig(t *testing.T) { t.Helper() ctx := context.Background() fclient := k8stools.GetTestClientWithObjects(o.predefinedObjects) - ac := getAssetsCache(ctx, fclient, o.cr) - got, err := generateServiceScrapeConfig(ctx, o.cr, o.sc, o.sc.Spec.Endpoints[0], 0, ac) + ac := getAssetsCache(ctx, fclient) + pos := &ParsedObjects{ + Namespace: o.cr.Namespace, + APIServerConfig: o.cr.Spec.APIServerConfig, + } + sp := &o.cr.Spec.CommonScrapeParams + got, err := generateServiceScrapeConfig(ctx, sp, pos, o.sc, o.sc.Spec.Endpoints[0], 0, ac) if err != nil { t.Errorf("cannot generate ServiceScrapeConfig, err: %e", err) return @@ -147,7 +152,7 @@ relabel_configs: - target_label: endpoint replacement: "8080" tls_config: - ca_file: /etc/vmagent-tls/certs/default_tls-secret_ca + ca_file: /tls/default_tls-secret_ca bearer_token_file: /var/run/token `, }) @@ -252,7 +257,7 @@ relabel_configs: - target_label: endpoint replacement: "8080" tls_config: - ca_file: /etc/vmagent-tls/certs/default_tls-secret_ca + ca_file: /tls/default_tls-secret_ca bearer_token_file: /var/run/token `, }) @@ -357,7 +362,7 @@ relabel_configs: - target_label: endpoint replacement: "8080" tls_config: - ca_file: /etc/vmagent-tls/certs/default_tls-secret_ca + ca_file: /tls/default_tls-secret_ca bearer_token_file: /var/run/token `, }) @@ -454,7 +459,7 @@ relabel_configs: - target_label: endpoint replacement: "8080" tls_config: - ca_file: /etc/vmagent-tls/certs/default_tls-secret_ca + ca_file: /tls/default_tls-secret_ca bearer_token_file: /var/run/token `, }) @@ -537,7 +542,7 @@ relabel_configs: - target_label: endpoint replacement: "8080" tls_config: - ca_file: /etc/vmagent-tls/certs/default_tls-secret_ca + ca_file: /tls/default_tls-secret_ca bearer_token_file: /var/run/token `, }) @@ -849,9 +854,9 @@ oauth2: proxy_url: http://oauth2-access-proxy tls_config: insecure_skip_verify: true - ca_file: /etc/vmagent-tls/certs/default_configmap_tls-cm_ca - cert_file: /etc/vmagent-tls/certs/default_tls_key - key_file: /etc/vmagent-tls/certs/default_tls_cert + ca_file: /tls/default_configmap_tls-cm_ca + cert_file: /tls/default_tls_key + key_file: /tls/default_tls_cert `, }) @@ -961,7 +966,7 @@ relabel_configs: target_label: node regex: .+ tls_config: - ca_file: /etc/vmagent-tls/certs/default_tls-secret_ca + ca_file: /tls/default_tls-secret_ca bearer_token_file: /var/run/token `, }) @@ -986,7 +991,7 @@ bearer_token_file: /var/run/token APIServerConfig: &vmv1beta1.APIServerConfig{ Host: "default-k8s-host", TLSConfig: &vmv1beta1.TLSConfig{ - CAFile: "/etc/vmagent-tls/certs/default_k8s_host_ca", + CAFile: "/tls/default_k8s_host_ca", }, }, }, @@ -1024,7 +1029,7 @@ kubernetes_sd_configs: - default api_server: default-k8s-host tls_config: - ca_file: /etc/vmagent-tls/certs/default_k8s_host_ca + ca_file: /tls/default_k8s_host_ca selectors: - role: endpoints label: dc=prod,env=dev,team=go @@ -1395,7 +1400,7 @@ relabel_configs: action: replace tls_config: ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt - cert_file: /etc/vmagent-tls/certs/default_tls-secret_cert + cert_file: /tls/default_tls-secret_cert `, }) diff --git a/internal/controller/operator/factory/vmagent/staticscrape.go b/internal/controller/operator/factory/vmscrapes/staticscrape.go similarity index 87% rename from internal/controller/operator/factory/vmagent/staticscrape.go rename to internal/controller/operator/factory/vmscrapes/staticscrape.go index 0788bcb38..98c9d08a9 100644 --- a/internal/controller/operator/factory/vmagent/staticscrape.go +++ b/internal/controller/operator/factory/vmscrapes/staticscrape.go @@ -1,4 +1,4 @@ -package vmagent +package vmscrapes import ( "context" @@ -12,14 +12,14 @@ import ( func generateStaticScrapeConfig( ctx context.Context, - cr *vmv1beta1.VMAgent, + sp *vmv1beta1.CommonScrapeParams, sc *vmv1beta1.VMStaticScrape, ep *vmv1beta1.TargetEndpoint, i int, ac *build.AssetsCache, ) (yaml.MapSlice, error) { spec := &sc.Spec - se := cr.Spec.CommonScrapeSecurityEnforcements + se := &sp.CommonScrapeSecurityEnforcements cfg := yaml.MapSlice{ { Key: "job_name", @@ -27,7 +27,7 @@ func generateStaticScrapeConfig( }, } - scrapeClass := getScrapeClass(spec.ScrapeClassName, cr) + scrapeClass := getScrapeClass(spec.ScrapeClassName, sp) if scrapeClass != nil { mergeEndpointAuthWithScrapeClass(&ep.EndpointAuth, scrapeClass) mergeEndpointRelabelingsWithScrapeClass(&ep.EndpointRelabelings, scrapeClass) @@ -48,9 +48,9 @@ func generateStaticScrapeConfig( ep.SeriesLimit = spec.SeriesLimit } if ep.ScrapeTimeout == "" { - ep.ScrapeTimeout = cr.Spec.ScrapeTimeout + ep.ScrapeTimeout = sp.ScrapeTimeout } - setScrapeIntervalToWithLimit(ctx, &ep.EndpointScrapeParams, cr) + setScrapeIntervalToWithLimit(ctx, &ep.EndpointScrapeParams, sp) cfg = addCommonScrapeParamsTo(cfg, ep.EndpointScrapeParams, se) @@ -66,7 +66,7 @@ func generateStaticScrapeConfig( for _, c := range ep.RelabelConfigs { relabelings = append(relabelings, generateRelabelConfig(c)) } - for _, trc := range cr.Spec.StaticScrapeRelabelTemplate { + for _, trc := range sp.StaticScrapeRelabelTemplate { relabelings = append(relabelings, generateRelabelConfig(trc)) } // Because of security risks, whenever enforcedNamespaceLabel is set, we want to append it to the diff --git a/internal/controller/operator/factory/vmagent/staticscrape_test.go b/internal/controller/operator/factory/vmscrapes/staticscrape_test.go similarity index 97% rename from internal/controller/operator/factory/vmagent/staticscrape_test.go rename to internal/controller/operator/factory/vmscrapes/staticscrape_test.go index 62f94ec12..97552f0e8 100644 --- a/internal/controller/operator/factory/vmagent/staticscrape_test.go +++ b/internal/controller/operator/factory/vmscrapes/staticscrape_test.go @@ -1,4 +1,4 @@ -package vmagent +package vmscrapes import ( "context" @@ -27,8 +27,9 @@ func Test_generateStaticScrapeConfig(t *testing.T) { t.Helper() ctx := context.Background() fclient := k8stools.GetTestClientWithObjects(o.predefinedObjects) - ac := getAssetsCache(ctx, fclient, o.cr) - got, err := generateStaticScrapeConfig(ctx, o.cr, o.sc, o.sc.Spec.TargetEndpoints[0], 0, ac) + ac := getAssetsCache(ctx, fclient) + sp := &o.cr.Spec.CommonScrapeParams + got, err := generateStaticScrapeConfig(ctx, sp, o.sc, o.sc.Spec.TargetEndpoints[0], 0, ac) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -363,9 +364,9 @@ proxy_basic_auth: password: proxy-password tls_config: insecure_skip_verify: true - ca_file: /etc/vmagent-tls/certs/default_tls-cfg_ca + ca_file: /tls/default_tls-cfg_ca cert_file: /tmp/cert-part - key_file: /etc/vmagent-tls/certs/default_tls-cfg_key + key_file: /tls/default_tls-cfg_key bearer_token: token-value basic_auth: username: admin diff --git a/internal/controller/operator/factory/vmagent/vmagent_scrapeconfig.go b/internal/controller/operator/factory/vmscrapes/vmscrapes.go similarity index 73% rename from internal/controller/operator/factory/vmagent/vmagent_scrapeconfig.go rename to internal/controller/operator/factory/vmscrapes/vmscrapes.go index 4a415fe7b..ff38b0d5b 100644 --- a/internal/controller/operator/factory/vmagent/vmagent_scrapeconfig.go +++ b/internal/controller/operator/factory/vmscrapes/vmscrapes.go @@ -1,8 +1,6 @@ -package vmagent +package vmscrapes import ( - "bytes" - "compress/gzip" "context" "fmt" "reflect" @@ -17,40 +15,26 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" 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/logger" "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/reconcile" ) -type parsedObjects struct { - serviceScrapes *build.ChildObjects[*vmv1beta1.VMServiceScrape] - podScrapes *build.ChildObjects[*vmv1beta1.VMPodScrape] - staticScrapes *build.ChildObjects[*vmv1beta1.VMStaticScrape] - nodeScrapes *build.ChildObjects[*vmv1beta1.VMNodeScrape] - probes *build.ChildObjects[*vmv1beta1.VMProbe] - scrapeConfigs *build.ChildObjects[*vmv1beta1.VMScrapeConfig] -} - -func (so *parsedObjects) updateMetrics(ctx context.Context) { - so.serviceScrapes.UpdateMetrics(ctx) - so.podScrapes.UpdateMetrics(ctx) - so.staticScrapes.UpdateMetrics(ctx) - so.nodeScrapes.UpdateMetrics(ctx) - so.probes.UpdateMetrics(ctx) - so.scrapeConfigs.UpdateMetrics(ctx) -} +const ( + kubeNodeEnvTemplate = "%{" + vmv1beta1.KubeNodeEnvName + "}" +) -func (so *parsedObjects) validateObjects(cr *vmv1beta1.VMAgent) { - so.serviceScrapes.ForEachCollectSkipInvalid(func(sc *vmv1beta1.VMServiceScrape) error { - if cr.Spec.ArbitraryFSAccessThroughSMs.Deny { +func (pos *ParsedObjects) ValidateObjects(sp *vmv1beta1.CommonScrapeParams) { + se := &sp.CommonScrapeSecurityEnforcements + pos.serviceScrapes.ForEachCollectSkipInvalid(func(sc *vmv1beta1.VMServiceScrape) error { + if se.ArbitraryFSAccessThroughSMs.Deny { for _, ep := range sc.Spec.Endpoints { if err := testForArbitraryFSAccess(ep.EndpointAuth); err != nil { return err } } } - if err := validateScrapeClassExists(sc.Spec.ScrapeClassName, cr); err != nil { + if err := validateScrapeClassExists(sc.Spec.ScrapeClassName, sp); err != nil { return err } if !build.MustSkipRuntimeValidation { @@ -59,15 +43,15 @@ func (so *parsedObjects) validateObjects(cr *vmv1beta1.VMAgent) { return nil }) - so.podScrapes.ForEachCollectSkipInvalid(func(sc *vmv1beta1.VMPodScrape) error { - if cr.Spec.ArbitraryFSAccessThroughSMs.Deny { + pos.podScrapes.ForEachCollectSkipInvalid(func(sc *vmv1beta1.VMPodScrape) error { + if se.ArbitraryFSAccessThroughSMs.Deny { for _, ep := range sc.Spec.PodMetricsEndpoints { if err := testForArbitraryFSAccess(ep.EndpointAuth); err != nil { return err } } } - if err := validateScrapeClassExists(sc.Spec.ScrapeClassName, cr); err != nil { + if err := validateScrapeClassExists(sc.Spec.ScrapeClassName, sp); err != nil { return err } if !build.MustSkipRuntimeValidation { @@ -75,15 +59,15 @@ func (so *parsedObjects) validateObjects(cr *vmv1beta1.VMAgent) { } return nil }) - so.staticScrapes.ForEachCollectSkipInvalid(func(sc *vmv1beta1.VMStaticScrape) error { - if cr.Spec.ArbitraryFSAccessThroughSMs.Deny { + pos.staticScrapes.ForEachCollectSkipInvalid(func(sc *vmv1beta1.VMStaticScrape) error { + if se.ArbitraryFSAccessThroughSMs.Deny { for _, ep := range sc.Spec.TargetEndpoints { if err := testForArbitraryFSAccess(ep.EndpointAuth); err != nil { return err } } } - if err := validateScrapeClassExists(sc.Spec.ScrapeClassName, cr); err != nil { + if err := validateScrapeClassExists(sc.Spec.ScrapeClassName, sp); err != nil { return err } if !build.MustSkipRuntimeValidation { @@ -92,13 +76,13 @@ func (so *parsedObjects) validateObjects(cr *vmv1beta1.VMAgent) { return nil }) - so.nodeScrapes.ForEachCollectSkipInvalid(func(sc *vmv1beta1.VMNodeScrape) error { - if cr.Spec.ArbitraryFSAccessThroughSMs.Deny { + pos.nodeScrapes.ForEachCollectSkipInvalid(func(sc *vmv1beta1.VMNodeScrape) error { + if se.ArbitraryFSAccessThroughSMs.Deny { if err := testForArbitraryFSAccess(sc.Spec.EndpointAuth); err != nil { return err } } - if err := validateScrapeClassExists(sc.Spec.ScrapeClassName, cr); err != nil { + if err := validateScrapeClassExists(sc.Spec.ScrapeClassName, sp); err != nil { return err } if !build.MustSkipRuntimeValidation { @@ -107,13 +91,13 @@ func (so *parsedObjects) validateObjects(cr *vmv1beta1.VMAgent) { return nil }) - so.probes.ForEachCollectSkipInvalid(func(sc *vmv1beta1.VMProbe) error { - if cr.Spec.ArbitraryFSAccessThroughSMs.Deny { + pos.probes.ForEachCollectSkipInvalid(func(sc *vmv1beta1.VMProbe) error { + if se.ArbitraryFSAccessThroughSMs.Deny { if err := testForArbitraryFSAccess(sc.Spec.EndpointAuth); err != nil { return err } } - if err := validateScrapeClassExists(sc.Spec.ScrapeClassName, cr); err != nil { + if err := validateScrapeClassExists(sc.Spec.ScrapeClassName, sp); err != nil { return err } if !build.MustSkipRuntimeValidation { @@ -122,14 +106,14 @@ func (so *parsedObjects) validateObjects(cr *vmv1beta1.VMAgent) { return nil }) - so.scrapeConfigs.ForEachCollectSkipInvalid(func(sc *vmv1beta1.VMScrapeConfig) error { + pos.scrapeConfigs.ForEachCollectSkipInvalid(func(sc *vmv1beta1.VMScrapeConfig) error { // TODO: @f41gh7 validate per configuration FS access - if cr.Spec.ArbitraryFSAccessThroughSMs.Deny { + if se.ArbitraryFSAccessThroughSMs.Deny { if err := testForArbitraryFSAccess(sc.Spec.EndpointAuth); err != nil { return err } } - if err := validateScrapeClassExists(sc.Spec.ScrapeClassName, cr); err != nil { + if err := validateScrapeClassExists(sc.Spec.ScrapeClassName, sp); err != nil { return err } if !build.MustSkipRuntimeValidation { @@ -139,170 +123,54 @@ func (so *parsedObjects) validateObjects(cr *vmv1beta1.VMAgent) { }) } -// CreateOrUpdateScrapeConfig builds scrape configuration for VMAgent -func CreateOrUpdateScrapeConfig(ctx context.Context, rclient client.Client, cr *vmv1beta1.VMAgent, childObject client.Object) error { - var prevCR *vmv1beta1.VMAgent - if cr.ParsedLastAppliedSpec != nil { - prevCR = cr.DeepCopy() - prevCR.Spec = *cr.ParsedLastAppliedSpec - } - ac := getAssetsCache(ctx, rclient, cr) - if err := createOrUpdateScrapeConfig(ctx, rclient, cr, prevCR, childObject, ac); err != nil { - return err - } - return nil -} - -func createOrUpdateScrapeConfig(ctx context.Context, rclient client.Client, cr, prevCR *vmv1beta1.VMAgent, childObject client.Object, ac *build.AssetsCache) error { - if cr.Spec.IngestOnlyMode { - return nil - } - // HACK: newPodSpec could load content into ac and it must be called - // before secret config reconcile - // - // TODO: @f41gh7 rewrite this section with VLAgent secret assets injection pattern - if _, err := newPodSpec(cr, ac); err != nil { - return err - } - - serviceScrapes, nsnServiceScrapes, err := selectServiceScrapes(ctx, cr, rclient) - if err != nil { - return fmt.Errorf("selecting ServiceScrapes failed: %w", err) - } - - podScrapes, nsnPodScrapes, err := selectPodScrapes(ctx, cr, rclient) - if err != nil { - return fmt.Errorf("selecting PodScrapes failed: %w", err) - } - - probes, nsnProbes, err := selectProbes(ctx, cr, rclient) - if err != nil { - return fmt.Errorf("selecting VMProbes failed: %w", err) - } - - nodeScrapes, nsnNodeScrapes, err := selectNodeScrapes(ctx, cr, rclient) - if err != nil { - return fmt.Errorf("selecting VMNodeScrapes failed: %w", err) - } - - staticScrapes, nsnStaticScrapes, err := selectStaticScrapes(ctx, cr, rclient) - if err != nil { - return fmt.Errorf("selecting VMStaticScrapes failed: %w", err) - } - - scrapeConfigs, nsnScrapeConfigs, err := selectScrapeConfigs(ctx, cr, rclient) - if err != nil { - return fmt.Errorf("selecting ScrapeConfigs failed: %w", err) - } - pos := &parsedObjects{ - serviceScrapes: build.NewChildObjects("vmservicescrape", serviceScrapes, nsnServiceScrapes), - podScrapes: build.NewChildObjects("vmpodscrape", podScrapes, nsnPodScrapes), - probes: build.NewChildObjects("vmprobe", probes, nsnProbes), - nodeScrapes: build.NewChildObjects("vmnodescrape", nodeScrapes, nsnNodeScrapes), - staticScrapes: build.NewChildObjects("vmstaticscrape", staticScrapes, nsnStaticScrapes), - scrapeConfigs: build.NewChildObjects("vmscrapeconfig", scrapeConfigs, nsnScrapeConfigs), - } - pos.validateObjects(cr) - - var additionalScrapeConfigs []byte - - if cr.Spec.AdditionalScrapeConfigs != nil { - sc, err := ac.LoadKeyFromSecret(cr.Namespace, cr.Spec.AdditionalScrapeConfigs) - if err != nil { - return fmt.Errorf("loading additional scrape configs from Secret failed: %w", err) - } - additionalScrapeConfigs = []byte(sc) - } - - // Update secret based on the most recent configuration. - generatedConfig, err := generateConfig( - ctx, - cr, - pos, - ac, - additionalScrapeConfigs, - ) - if err != nil { - return fmt.Errorf("generating config for vmagent failed: %w", err) - } - - for kind, secret := range ac.GetOutput() { - var prevSecretMeta *metav1.ObjectMeta - if prevCR != nil { - prevSecretMeta = ptr.To(build.ResourceMeta(kind, prevCR)) - } - if kind == build.SecretConfigResourceKind { - // Compress config to avoid 1mb secret limit for a while - var buf bytes.Buffer - if err = gzipConfig(&buf, generatedConfig); err != nil { - return fmt.Errorf("cannot gzip config for vmagent: %w", err) - } - secret.Data[scrapeGzippedFilename] = buf.Bytes() - } - secret.ObjectMeta = build.ResourceMeta(kind, cr) - secret.Annotations = map[string]string{ - "generated": "true", - } - if err := reconcile.Secret(ctx, rclient, &secret, prevSecretMeta); err != nil { - return err - } - } - - if err := pos.updateStatusesForScrapeObjects(ctx, rclient, cr, childObject); err != nil { - return err - } - - return nil -} - -func (pos *parsedObjects) updateStatusesForScrapeObjects(ctx context.Context, rclient client.Client, cr *vmv1beta1.VMAgent, childObject client.Object) error { - parentObject := fmt.Sprintf("%s.%s.vmagent", cr.Name, cr.Namespace) +// UpdateStatusesForScrapeObjects updates status of either selected childObject or all child objects +func (pos *ParsedObjects) UpdateStatusesForScrapeObjects(ctx context.Context, rclient client.Client, parentName string, childObject client.Object) error { pos.updateMetrics(ctx) if childObject != nil && !reflect.ValueOf(childObject).IsNil() { // fast path switch t := childObject.(type) { case *vmv1beta1.VMStaticScrape: if o := pos.staticScrapes.Get(t); o != nil { - return reconcile.StatusForChildObjects(ctx, rclient, parentObject, []*vmv1beta1.VMStaticScrape{o}) + return reconcile.StatusForChildObjects(ctx, rclient, parentName, []*vmv1beta1.VMStaticScrape{o}) } case *vmv1beta1.VMProbe: if o := pos.probes.Get(t); o != nil { - return reconcile.StatusForChildObjects(ctx, rclient, parentObject, []*vmv1beta1.VMProbe{o}) + return reconcile.StatusForChildObjects(ctx, rclient, parentName, []*vmv1beta1.VMProbe{o}) } case *vmv1beta1.VMScrapeConfig: if o := pos.scrapeConfigs.Get(t); o != nil { - return reconcile.StatusForChildObjects(ctx, rclient, parentObject, []*vmv1beta1.VMScrapeConfig{o}) + return reconcile.StatusForChildObjects(ctx, rclient, parentName, []*vmv1beta1.VMScrapeConfig{o}) } case *vmv1beta1.VMNodeScrape: if o := pos.nodeScrapes.Get(t); o != nil { - return reconcile.StatusForChildObjects(ctx, rclient, parentObject, []*vmv1beta1.VMNodeScrape{o}) + return reconcile.StatusForChildObjects(ctx, rclient, parentName, []*vmv1beta1.VMNodeScrape{o}) } case *vmv1beta1.VMPodScrape: if o := pos.podScrapes.Get(t); o != nil { - return reconcile.StatusForChildObjects(ctx, rclient, parentObject, []*vmv1beta1.VMPodScrape{o}) + return reconcile.StatusForChildObjects(ctx, rclient, parentName, []*vmv1beta1.VMPodScrape{o}) } case *vmv1beta1.VMServiceScrape: if o := pos.serviceScrapes.Get(t); o != nil { - return reconcile.StatusForChildObjects(ctx, rclient, parentObject, []*vmv1beta1.VMServiceScrape{o}) + return reconcile.StatusForChildObjects(ctx, rclient, parentName, []*vmv1beta1.VMServiceScrape{o}) } } } - if err := reconcile.StatusForChildObjects(ctx, rclient, parentObject, pos.serviceScrapes.All()); err != nil { + if err := reconcile.StatusForChildObjects(ctx, rclient, parentName, pos.serviceScrapes.All()); err != nil { return fmt.Errorf("cannot update statuses for service scrape objects: %w", err) } - if err := reconcile.StatusForChildObjects(ctx, rclient, parentObject, pos.podScrapes.All()); err != nil { + if err := reconcile.StatusForChildObjects(ctx, rclient, parentName, pos.podScrapes.All()); err != nil { return fmt.Errorf("cannot update statuses for pod scrape objects: %w", err) } - if err := reconcile.StatusForChildObjects(ctx, rclient, parentObject, pos.nodeScrapes.All()); err != nil { + if err := reconcile.StatusForChildObjects(ctx, rclient, parentName, pos.nodeScrapes.All()); err != nil { return fmt.Errorf("cannot update statuses for node scrape objects: %w", err) } - if err := reconcile.StatusForChildObjects(ctx, rclient, parentObject, pos.probes.All()); err != nil { + if err := reconcile.StatusForChildObjects(ctx, rclient, parentName, pos.probes.All()); err != nil { return fmt.Errorf("cannot update statuses for probe scrape objects: %w", err) } - if err := reconcile.StatusForChildObjects(ctx, rclient, parentObject, pos.staticScrapes.All()); err != nil { + if err := reconcile.StatusForChildObjects(ctx, rclient, parentName, pos.staticScrapes.All()); err != nil { return fmt.Errorf("cannot update statuses for static scrape objects: %w", err) } - if err := reconcile.StatusForChildObjects(ctx, rclient, parentObject, pos.scrapeConfigs.All()); err != nil { + if err := reconcile.StatusForChildObjects(ctx, rclient, parentName, pos.scrapeConfigs.All()); err != nil { return fmt.Errorf("cannot update statuses for scrapeconfig scrape objects: %w", err) } return nil @@ -337,21 +205,12 @@ func testForArbitraryFSAccess(e vmv1beta1.EndpointAuth) error { return nil } -func gzipConfig(buf *bytes.Buffer, conf []byte) error { - w := gzip.NewWriter(buf) - defer w.Close() - if _, err := w.Write(conf); err != nil { - return err - } - return nil -} - -func setScrapeIntervalToWithLimit(ctx context.Context, dst *vmv1beta1.EndpointScrapeParams, cr *vmv1beta1.VMAgent) { +func setScrapeIntervalToWithLimit(ctx context.Context, dst *vmv1beta1.EndpointScrapeParams, sp *vmv1beta1.CommonScrapeParams) { if dst.ScrapeInterval == "" { dst.ScrapeInterval = dst.Interval } - originInterval, minIntervalStr, maxIntervalStr := dst.ScrapeInterval, cr.Spec.MinScrapeInterval, cr.Spec.MaxScrapeInterval + originInterval, minIntervalStr, maxIntervalStr := dst.ScrapeInterval, sp.MinScrapeInterval, sp.MaxScrapeInterval if originInterval == "" || (minIntervalStr == nil && maxIntervalStr == nil) { // fast path return @@ -398,54 +257,61 @@ const ( var invalidLabelCharRE = regexp.MustCompile(`[^a-zA-Z0-9_]`) -func generateConfig( +// GenerateConfig generates yaml scrape configuration from collected scrape objects +func (pos *ParsedObjects) GenerateConfig( ctx context.Context, - cr *vmv1beta1.VMAgent, - pos *parsedObjects, + sp *vmv1beta1.CommonScrapeParams, ac *build.AssetsCache, - additionalScrapeConfigs []byte, ) ([]byte, error) { + var additionalScrapeConfigs []byte + if sp.AdditionalScrapeConfigs != nil { + sc, err := ac.LoadKeyFromSecret(pos.Namespace, sp.AdditionalScrapeConfigs) + if err != nil { + return nil, fmt.Errorf("loading additional scrape configs from Secret failed: %w", err) + } + additionalScrapeConfigs = []byte(sc) + } cfg := yaml.MapSlice{} - if !config.IsClusterWideAccessAllowed() && cr.IsOwnsServiceAccount() { + if !pos.HasClusterWideAccess { logger.WithContext(ctx).Info("Setting discovery for the single namespace only." + "Since operator launched with set WATCH_NAMESPACE param. " + "Set custom ServiceAccountName property for VMAgent if needed.") - cr.Spec.IgnoreNamespaceSelectors = true + sp.IgnoreNamespaceSelectors = true } scrapeInterval := defaultScrapeInterval - if cr.Spec.ScrapeInterval != "" { - scrapeInterval = cr.Spec.ScrapeInterval + if sp.ScrapeInterval != "" { + scrapeInterval = sp.ScrapeInterval } globalItems := yaml.MapSlice{ {Key: "scrape_interval", Value: scrapeInterval}, - {Key: "external_labels", Value: buildExternalLabels(cr)}, + {Key: "external_labels", Value: stringMapToMapSlice(pos.ExternalLabels)}, } - if cr.Spec.SampleLimit > 0 { + if sp.SampleLimit > 0 { globalItems = append(globalItems, yaml.MapItem{ Key: "sample_limit", - Value: cr.Spec.SampleLimit, + Value: sp.SampleLimit, }) } - if cr.Spec.ScrapeTimeout != "" { + if sp.ScrapeTimeout != "" { globalItems = append(globalItems, yaml.MapItem{ Key: "scrape_timeout", - Value: cr.Spec.ScrapeTimeout, + Value: sp.ScrapeTimeout, }) } - if len(cr.Spec.GlobalScrapeMetricRelabelConfigs) > 0 { + if len(sp.GlobalScrapeMetricRelabelConfigs) > 0 { globalItems = append(globalItems, yaml.MapItem{ Key: "metric_relabel_configs", - Value: cr.Spec.GlobalScrapeMetricRelabelConfigs, + Value: sp.GlobalScrapeMetricRelabelConfigs, }) } - if len(cr.Spec.GlobalScrapeRelabelConfigs) > 0 { + if len(sp.GlobalScrapeRelabelConfigs) > 0 { globalItems = append(globalItems, yaml.MapItem{ Key: "relabel_configs", - Value: cr.Spec.GlobalScrapeRelabelConfigs, + Value: sp.GlobalScrapeRelabelConfigs, }) } @@ -459,7 +325,8 @@ func generateConfig( for i, ep := range sc.Spec.Endpoints { s, err := generateServiceScrapeConfig( ctx, - cr, + sp, + pos, sc, ep, i, ac, @@ -481,7 +348,8 @@ func generateConfig( for i, ep := range sc.Spec.PodMetricsEndpoints { s, err := generatePodScrapeConfig( ctx, - cr, + sp, + pos, sc, ep, i, ac, ) @@ -500,7 +368,8 @@ func generateConfig( err = pos.probes.ForEachCollectSkipNotFound(func(sc *vmv1beta1.VMProbe) error { s, err := generateProbeConfig( ctx, - cr, + sp, + pos, sc, ac, ) @@ -517,7 +386,8 @@ func generateConfig( err = pos.nodeScrapes.ForEachCollectSkipNotFound(func(sc *vmv1beta1.VMNodeScrape) error { s, err := generateNodeScrapeConfig( ctx, - cr, + sp, + pos, sc, ac, ) @@ -537,7 +407,7 @@ func generateConfig( for i, ep := range sc.Spec.TargetEndpoints { s, err := generateStaticScrapeConfig( ctx, - cr, + sp, sc, ep, i, ac, @@ -557,7 +427,7 @@ func generateConfig( err = pos.scrapeConfigs.ForEachCollectSkipNotFound(func(sc *vmv1beta1.VMScrapeConfig) error { s, err := generateScrapeConfig( ctx, - cr, + sp, sc, ac, ) @@ -578,8 +448,8 @@ func generateConfig( } var inlineScrapeConfigsYaml []yaml.MapSlice - if len(cr.Spec.InlineScrapeConfig) > 0 { - if err := yaml.Unmarshal([]byte(cr.Spec.InlineScrapeConfig), &inlineScrapeConfigsYaml); err != nil { + if len(sp.InlineScrapeConfig) > 0 { + if err := yaml.Unmarshal([]byte(sp.InlineScrapeConfig), &inlineScrapeConfigsYaml); err != nil { return nil, fmt.Errorf("unmarshalling inline additional scrape configs failed: %w", err) } } @@ -672,7 +542,8 @@ func addAttachMetadata(dst yaml.MapSlice, am *vmv1beta1.AttachMetadata, role str return dst } -func addRelabelConfigs(dst []yaml.MapSlice, rcs []*vmv1beta1.RelabelConfig) []yaml.MapSlice { +// AddRelabelConfigs adds relabel configuration to yaml +func AddRelabelConfigs(dst []yaml.MapSlice, rcs []*vmv1beta1.RelabelConfig) []yaml.MapSlice { for i := range rcs { rc := rcs[i] if rc.IsEmpty() { @@ -907,38 +778,6 @@ func enforceNamespaceLabel(relabelings []yaml.MapSlice, namespace, enforcedNames }) } -func buildExternalLabels(cr *vmv1beta1.VMAgent) yaml.MapSlice { - m := map[string]string{} - sp := cr.Spec.CommonScrapeParams - - // Use "prometheus" external label name by default if field is missing. - // in case of migration from prometheus to vmagent, it helps to have same labels - // Do not add external label if field is set to empty string. - prometheusExternalLabelName := "prometheus" - var labelName *string - if sp.ExternalLabelName != nil { - labelName = sp.ExternalLabelName - } else if sp.VMAgentExternalLabelName != nil { - labelName = sp.VMAgentExternalLabelName - } - if labelName != nil { - if *labelName != "" { - prometheusExternalLabelName = *labelName - } else { - prometheusExternalLabelName = "" - } - } - - if prometheusExternalLabelName != "" { - m[prometheusExternalLabelName] = fmt.Sprintf("%s/%s", cr.Namespace, cr.Name) - } - - for n, v := range sp.ExternalLabels { - m[n] = v - } - return stringMapToMapSlice(m) -} - func buildVMScrapeParams(namespace string, cfg *vmv1beta1.VMScrapeParams, ac *build.AssetsCache) (yaml.MapSlice, error) { var r yaml.MapSlice if cfg == nil { @@ -1020,7 +859,7 @@ func addSelectorToRelabelingFor(relabelings []yaml.MapSlice, typeName string, se return relabelings } -func addCommonScrapeParamsTo(cfg yaml.MapSlice, cs vmv1beta1.EndpointScrapeParams, se vmv1beta1.CommonScrapeSecurityEnforcements) yaml.MapSlice { +func addCommonScrapeParamsTo(cfg yaml.MapSlice, cs vmv1beta1.EndpointScrapeParams, se *vmv1beta1.CommonScrapeSecurityEnforcements) yaml.MapSlice { hl := honorLabels(cs.HonorLabels, se.OverrideHonorLabels) cfg = append(cfg, yaml.MapItem{ Key: "honor_labels", @@ -1075,7 +914,7 @@ func addCommonScrapeParamsTo(cfg yaml.MapSlice, cs vmv1beta1.EndpointScrapeParam return cfg } -func addMetricRelabelingsTo(cfg yaml.MapSlice, src []*vmv1beta1.RelabelConfig, se vmv1beta1.CommonScrapeSecurityEnforcements) yaml.MapSlice { +func addMetricRelabelingsTo(cfg yaml.MapSlice, src []*vmv1beta1.RelabelConfig, se *vmv1beta1.CommonScrapeSecurityEnforcements) yaml.MapSlice { if len(src) == 0 { return cfg } @@ -1130,29 +969,16 @@ func addEndpointAuthTo(cfg yaml.MapSlice, ea *vmv1beta1.EndpointAuth, namespace return cfg, nil } -func getAssetsCache(ctx context.Context, rclient client.Client, cr *vmv1beta1.VMAgent) *build.AssetsCache { - cfg := map[build.ResourceKind]*build.ResourceCfg{ - build.SecretConfigResourceKind: { - MountDir: vmAgentConfDir, - SecretName: build.ResourceName(build.SecretConfigResourceKind, cr), - }, - build.TLSAssetsResourceKind: { - MountDir: tlsAssetsDir, - SecretName: build.ResourceName(build.TLSAssetsResourceKind, cr), - }, - } - return build.NewAssetsCache(ctx, rclient, cfg) -} -func validateScrapeClassExists(scrapeClassName *string, cr *vmv1beta1.VMAgent) error { +func validateScrapeClassExists(scrapeClassName *string, sp *vmv1beta1.CommonScrapeParams) error { if scrapeClassName == nil { return nil } - for _, sc := range cr.Spec.ScrapeClasses { + for _, sc := range sp.ScrapeClasses { if sc.Name == *scrapeClassName { return nil } } - return fmt.Errorf("scrape class %q not found in VMAgent %s/%s", *scrapeClassName, cr.Namespace, cr.Name) + return fmt.Errorf("scrape class %q not supported", *scrapeClassName) } func mergeEndpointAuthWithScrapeClass(authz *vmv1beta1.EndpointAuth, scrapeClass *vmv1beta1.ScrapeClass) { @@ -1298,9 +1124,9 @@ func mergeTLSConfigs(left, right *vmv1beta1.TLSConfig) *vmv1beta1.TLSConfig { return left } -func getScrapeClass(name *string, vmagent *vmv1beta1.VMAgent) *vmv1beta1.ScrapeClass { +func getScrapeClass(name *string, sp *vmv1beta1.CommonScrapeParams) *vmv1beta1.ScrapeClass { var defaultClass *vmv1beta1.ScrapeClass - for _, scrapeClass := range vmagent.Spec.ScrapeClasses { + for _, scrapeClass := range sp.ScrapeClasses { if ptr.Deref(name, "") == scrapeClass.Name { return &scrapeClass } diff --git a/internal/controller/operator/factory/vmscrapes/vmscrapes_test.go b/internal/controller/operator/factory/vmscrapes/vmscrapes_test.go new file mode 100644 index 000000000..0d06515b7 --- /dev/null +++ b/internal/controller/operator/factory/vmscrapes/vmscrapes_test.go @@ -0,0 +1,110 @@ +package vmscrapes + +import ( + "context" + "encoding/json" + "testing" + + "github.com/stretchr/testify/assert" + "gopkg.in/yaml.v2" + "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" +) + +func Test_generateRelabelConfig(t *testing.T) { + f := func(rc *vmv1beta1.RelabelConfig, want string) { + // related fields only filled during json unmarshal + j, err := json.Marshal(rc) + if err != nil { + t.Fatalf("cannot serialize relabelConfig: %s", err) + } + var rlbCfg vmv1beta1.RelabelConfig + if err := json.Unmarshal(j, &rlbCfg); err != nil { + t.Fatalf("cannot parse relabelConfig: %s", err) + } + got := generateRelabelConfig(&rlbCfg) + gotBytes, err := yaml.Marshal(got) + if err != nil { + t.Errorf("cannot marshal generateRelabelConfig to yaml: %e", err) + return + } + assert.Equal(t, want, string(gotBytes)) + } + + // ok base cfg + f(&vmv1beta1.RelabelConfig{ + TargetLabel: "address", + SourceLabels: []string{"__address__"}, + Action: "replace", + }, `source_labels: +- __address__ +target_label: address +action: replace +`) + + // ok base with underscore + f(&vmv1beta1.RelabelConfig{ + UnderScoreTargetLabel: "address", + UnderScoreSourceLabels: []string{"__address__"}, + Action: "replace", + }, `source_labels: +- __address__ +target_label: address +action: replace +`) + + // ok base with graphite match labels + f(&vmv1beta1.RelabelConfig{ + UnderScoreTargetLabel: "address", + UnderScoreSourceLabels: []string{"__address__"}, + Action: "graphite", + Labels: map[string]string{"job": "$1", "instance": "${2}:8080"}, + Match: `foo.*.*.bar`, + }, `source_labels: +- __address__ +target_label: address +action: graphite +match: foo.*.*.bar +labels: + instance: ${2}:8080 + job: $1 +`) + + // with empty replacement and separator + f(&vmv1beta1.RelabelConfig{ + UnderScoreTargetLabel: "address", + UnderScoreSourceLabels: []string{"__address__"}, + Action: "graphite", + Labels: map[string]string{"job": "$1", "instance": "${2}:8080"}, + Match: `foo.*.*.bar`, + Separator: ptr.To(""), + Replacement: ptr.To(""), + }, `source_labels: +- __address__ +separator: "" +target_label: address +replacement: "" +action: graphite +match: foo.*.*.bar +labels: + instance: ${2}:8080 + job: $1 +`) +} + +func getAssetsCache(ctx context.Context, rclient client.Client) *build.AssetsCache { + cfg := map[build.ResourceKind]*build.ResourceCfg{ + build.SecretConfigResourceKind: { + MountDir: "/conf", + SecretName: "conf-secret", + }, + build.TLSAssetsResourceKind: { + MountDir: "/tls", + SecretName: "tls-secret", + }, + } + return build.NewAssetsCache(ctx, rclient, cfg) +} diff --git a/internal/controller/operator/factory/vmsingle/rbac.go b/internal/controller/operator/factory/vmsingle/rbac.go new file mode 100644 index 000000000..d32ca36ce --- /dev/null +++ b/internal/controller/operator/factory/vmsingle/rbac.go @@ -0,0 +1,300 @@ +package vmsingle + +import ( + "context" + "fmt" + + rbacv1 "k8s.io/api/rbac/v1" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + + vmv1beta1 "github.com/VictoriaMetrics/operator/api/operator/v1beta1" + "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/finalize" + "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/reconcile" +) + +var ( + singleNSPolicyRules = []rbacv1.PolicyRule{ + { + APIGroups: []string{"discovery.k8s.io"}, + Verbs: []string{ + "get", + "list", + "watch", + }, + Resources: []string{ + "endpointslices", + }, + }, + { + APIGroups: []string{""}, + Verbs: []string{ + "get", + "list", + "watch", + }, + Resources: []string{ + "services", + "endpoints", + "pods", + "secrets", + "configmaps", + }, + }, + { + APIGroups: []string{"networking.k8s.io", "extensions"}, + Verbs: []string{ + "get", + "list", + "watch", + }, + Resources: []string{ + "ingresses", + }, + }, + } + clusterWidePolicyRules = []rbacv1.PolicyRule{ + { + APIGroups: []string{"discovery.k8s.io"}, + Verbs: []string{ + "get", + "list", + "watch", + }, + Resources: []string{ + "endpointslices", + }, + }, + { + APIGroups: []string{""}, + Verbs: []string{ + "get", + "list", + "watch", + }, + Resources: []string{ + "nodes", + "nodes/metrics", + "nodes/proxy", + "services", + "endpoints", + "pods", + "configmaps", + "namespaces", + "secrets", + }, + }, + { + APIGroups: []string{"networking.k8s.io", "extensions"}, + Verbs: []string{ + "get", + "list", + "watch", + }, + Resources: []string{ + "ingresses", + }, + }, + { + NonResourceURLs: []string{"/metrics", "/metrics/resources", "/metrics/slis"}, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{"route.openshift.io", "image.openshift.io"}, + Verbs: []string{ + "get", + }, + Resources: []string{ + "routers/metrics", "registry/metrics", + }, + }, + } +) + +// createK8sAPIAccess - creates RBAC access rules for vmsingle +func createK8sAPIAccess(ctx context.Context, rclient client.Client, cr, prevCR *vmv1beta1.VMSingle, clusterWide bool) error { + if err := migrateRBAC(ctx, rclient, cr, clusterWide); err != nil { + return fmt.Errorf("cannot perform RBAC migration: %w", err) + } + if clusterWide { + if err := ensureCRExist(ctx, rclient, cr, prevCR); err != nil { + return fmt.Errorf("cannot ensure state of vmsingle's cluster role: %w", err) + } + if err := ensureCRBExist(ctx, rclient, cr, prevCR); err != nil { + return fmt.Errorf("cannot ensure state of vmsingle's cluster role binding: %w", err) + } + return nil + } + + if err := ensureRoleExist(ctx, rclient, cr, prevCR); err != nil { + return fmt.Errorf("cannot ensure state of vmsingle's cluster role: %w", err) + } + if err := ensureRBExist(ctx, rclient, cr, prevCR); err != nil { + return fmt.Errorf("cannot ensure state of vmsingle's role binding: %w", err) + } + + return nil +} + +func ensureCRExist(ctx context.Context, rclient client.Client, cr, prevCR *vmv1beta1.VMSingle) error { + var prevClusterRole *rbacv1.ClusterRole + if prevCR != nil { + prevClusterRole = buildCR(prevCR) + } + return reconcile.ClusterRole(ctx, rclient, buildCR(cr), prevClusterRole) +} + +func ensureCRBExist(ctx context.Context, rclient client.Client, cr, prevCR *vmv1beta1.VMSingle) error { + var prevCRB *rbacv1.ClusterRoleBinding + if prevCR != nil { + prevCRB = buildCRB(prevCR) + } + return reconcile.ClusterRoleBinding(ctx, rclient, buildCRB(cr), prevCRB) +} + +// migrateRBAC deletes incorrectly formatted resource names +// see https://github.com/VictoriaMetrics/operator/issues/891 +// and https://github.com/VictoriaMetrics/operator/pull/1176 +func migrateRBAC(ctx context.Context, rclient client.Client, cr *vmv1beta1.VMSingle, clusterWide bool) error { + const prevNamingPrefix = "monitoring:vmsingle-cluster-access-" + prevVersionName := prevNamingPrefix + cr.Name + currentVersionName := cr.GetClusterRoleName() + owner := cr.AsOwner() + + // explicitly set namespace via ObjetMeta for unit tests + toMigrateObjects := []client.Object{ + &rbacv1.ClusterRole{ObjectMeta: metav1.ObjectMeta{Namespace: cr.Namespace}}, + &rbacv1.ClusterRoleBinding{ObjectMeta: metav1.ObjectMeta{Namespace: cr.Namespace}}, + } + if !clusterWide { + toMigrateObjects = []client.Object{ + &rbacv1.Role{ObjectMeta: metav1.ObjectMeta{Namespace: cr.Namespace}}, + &rbacv1.RoleBinding{ObjectMeta: metav1.ObjectMeta{Namespace: cr.Namespace}}, + } + } + + for _, obj := range toMigrateObjects { + if err := rclient.Get(ctx, types.NamespacedName{Namespace: cr.Namespace, Name: currentVersionName}, obj); err != nil { + if !k8serrors.IsNotFound(err) { + return fmt.Errorf("cannot get object: %w", err) + } + // update name with prev version formatting + obj.SetName(prevVersionName) + if err := finalize.SafeDeleteWithFinalizer(ctx, rclient, obj, &owner); err != nil { + return fmt.Errorf("cannot safe delete obj : %w", err) + } + } + } + + return nil +} + +func buildCRB(cr *vmv1beta1.VMSingle) *rbacv1.ClusterRoleBinding { + r := &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: cr.GetClusterRoleName(), + Namespace: cr.GetNamespace(), + Labels: cr.FinalLabels(), + Annotations: cr.FinalAnnotations(), + Finalizers: []string{vmv1beta1.FinalizerName}, + }, + Subjects: []rbacv1.Subject{ + { + Kind: rbacv1.ServiceAccountKind, + Name: cr.GetServiceAccountName(), + Namespace: cr.GetNamespace(), + }, + }, + RoleRef: rbacv1.RoleRef{ + APIGroup: rbacv1.GroupName, + Name: cr.GetClusterRoleName(), + Kind: "ClusterRole", + }, + } + owner := cr.AsCRDOwner() + if owner != nil { + // Kubernetes does not allow namespace-scoped resources to own cluster-scoped resources, + // use crd instead + r.OwnerReferences = []metav1.OwnerReference{*owner} + } + return r +} + +func buildCR(cr *vmv1beta1.VMSingle) *rbacv1.ClusterRole { + r := &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: cr.GetClusterRoleName(), + Namespace: cr.GetNamespace(), + Labels: cr.FinalLabels(), + Annotations: cr.FinalAnnotations(), + Finalizers: []string{vmv1beta1.FinalizerName}, + }, + Rules: clusterWidePolicyRules, + } + owner := cr.AsCRDOwner() + if owner != nil { + // Kubernetes does not allow namespace-scoped resources to own cluster-scoped resources, + // use crd instead + r.OwnerReferences = []metav1.OwnerReference{*owner} + } + return r +} + +func ensureRoleExist(ctx context.Context, rclient client.Client, cr, prevCR *vmv1beta1.VMSingle) error { + nr := buildRole(cr) + var prevRole *rbacv1.Role + if prevCR != nil { + prevRole = buildRole(prevCR) + } + return reconcile.Role(ctx, rclient, nr, prevRole) +} + +func ensureRBExist(ctx context.Context, rclient client.Client, cr, prevCR *vmv1beta1.VMSingle) error { + rb := buildRB(cr) + var prevRB *rbacv1.RoleBinding + if prevCR != nil { + prevRB = buildRB(prevCR) + } + return reconcile.RoleBinding(ctx, rclient, rb, prevRB) +} + +func buildRole(cr *vmv1beta1.VMSingle) *rbacv1.Role { + return &rbacv1.Role{ + ObjectMeta: metav1.ObjectMeta{ + Name: cr.GetClusterRoleName(), + Namespace: cr.GetNamespace(), + Labels: cr.FinalLabels(), + Annotations: cr.FinalAnnotations(), + Finalizers: []string{vmv1beta1.FinalizerName}, + OwnerReferences: []metav1.OwnerReference{cr.AsOwner()}, + }, + Rules: singleNSPolicyRules, + } +} + +func buildRB(cr *vmv1beta1.VMSingle) *rbacv1.RoleBinding { + return &rbacv1.RoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: cr.GetClusterRoleName(), + Namespace: cr.GetNamespace(), + Labels: cr.FinalLabels(), + Annotations: cr.FinalAnnotations(), + Finalizers: []string{vmv1beta1.FinalizerName}, + OwnerReferences: []metav1.OwnerReference{cr.AsOwner()}, + }, + Subjects: []rbacv1.Subject{ + { + Kind: rbacv1.ServiceAccountKind, + Name: cr.GetServiceAccountName(), + Namespace: cr.GetNamespace(), + }, + }, + RoleRef: rbacv1.RoleRef{ + APIGroup: rbacv1.GroupName, + Name: cr.GetClusterRoleName(), + Kind: "Role", + }, + } +} diff --git a/internal/controller/operator/factory/vmsingle/rbac_test.go b/internal/controller/operator/factory/vmsingle/rbac_test.go new file mode 100644 index 000000000..fe3fece92 --- /dev/null +++ b/internal/controller/operator/factory/vmsingle/rbac_test.go @@ -0,0 +1,73 @@ +package vmsingle + +import ( + "context" + "testing" + + 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" + + vmv1beta1 "github.com/VictoriaMetrics/operator/api/operator/v1beta1" + "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/k8stools" +) + +func TestCreateVMSingleClusterAccess(t *testing.T) { + f := func(cr *vmv1beta1.VMSingle, predefinedObjects []runtime.Object) { + t.Helper() + fclient := k8stools.GetTestClientWithObjects(predefinedObjects) + if err := createK8sAPIAccess(context.TODO(), fclient, cr, nil, true); err != nil { + t.Errorf("createK8sAPIAccess() error = %v", err) + } + } + + // ok create default rbac + f(&vmv1beta1.VMSingle{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: "rbac-test", + }, + Spec: vmv1beta1.VMSingleSpec{}, + }, nil) + + // ok with exist rbac + f(&vmv1beta1.VMSingle{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default-2", + Name: "rbac-test", + }, + Spec: vmv1beta1.VMSingleSpec{}, + }, []runtime.Object{ + &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "monitoring:vmsingle-cluster-access-rbac-test", + Namespace: "default-2", + Labels: map[string]string{ + "app.kubernetes.io/name": "vmsingle", + "app.kubernetes.io/instance": "rbac-test", + "app.kubernetes.io/component": "monitoring", + "managed-by": "vm-operator", + }, + }, + }, + &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: "monitoring:vmsingle-cluster-access-rbac-test", + Namespace: "default-2", + Labels: map[string]string{ + "app.kubernetes.io/name": "vmsingle", + "app.kubernetes.io/instance": "rbac-test", + "app.kubernetes.io/component": "monitoring", + "managed-by": "vm-operator", + }, + }, + }, + &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: "vmsingle-rbac-test", + Namespace: "default-2", + }, + }, + }) +} diff --git a/internal/controller/operator/factory/vmsingle/scrapes_test.go b/internal/controller/operator/factory/vmsingle/scrapes_test.go new file mode 100644 index 000000000..b15df3e2f --- /dev/null +++ b/internal/controller/operator/factory/vmsingle/scrapes_test.go @@ -0,0 +1,2169 @@ +package vmsingle + +import ( + "bytes" + "compress/gzip" + "context" + "io" + "testing" + + "github.com/stretchr/testify/assert" + 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" + "sigs.k8s.io/controller-runtime/pkg/client" + + 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" +) + +func TestCreateOrUpdateScrapeConfig(t *testing.T) { + type opts struct { + cr *vmv1beta1.VMSingle + cfgMutator func(c *config.BaseOperatorConf) + predefinedObjects []runtime.Object + wantConfig string + } + + f := func(o opts) { + t.Helper() + ctx := context.TODO() + testClient := k8stools.GetTestClientWithObjects(o.predefinedObjects) + cfg := config.MustGetBaseConfig() + if o.cfgMutator != nil { + defaultCfg := *cfg + o.cfgMutator(cfg) + defer func() { + *config.MustGetBaseConfig() = defaultCfg + }() + } + build.AddDefaults(testClient.Scheme()) + ac := getAssetsCache(ctx, testClient, o.cr) + if err := createOrUpdateScrapeConfig(ctx, testClient, o.cr, nil, nil, ac); err != nil { + t.Errorf("CreateOrUpdateConfigurationSecret() error = %v", err) + } + var expectSecret corev1.Secret + if err := testClient.Get(ctx, types.NamespacedName{Namespace: o.cr.Namespace, Name: o.cr.PrefixedName()}, &expectSecret); err != nil { + t.Fatalf("cannot get vmsingle config secret: %s", err) + } + gotCfg := expectSecret.Data[scrapeGzippedFilename] + cfgB := bytes.NewBuffer(gotCfg) + gr, err := gzip.NewReader(cfgB) + if err != nil { + t.Fatalf("er: %s", err) + } + data, err := io.ReadAll(gr) + if err != nil { + t.Fatalf("cannot read cfg: %s", err) + } + gr.Close() + assert.Equal(t, o.wantConfig, string(data)) + } + + // complete test + f(opts{ + cr: &vmv1beta1.VMSingle{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "default", + }, + Spec: vmv1beta1.VMSingleSpec{ + CommonScrapeParams: vmv1beta1.CommonScrapeParams{ + IngestOnlyMode: ptr.To(false), + ServiceScrapeNamespaceSelector: &metav1.LabelSelector{}, + ServiceScrapeSelector: &metav1.LabelSelector{}, + PodScrapeSelector: &metav1.LabelSelector{}, + PodScrapeNamespaceSelector: &metav1.LabelSelector{}, + NodeScrapeNamespaceSelector: &metav1.LabelSelector{}, + NodeScrapeSelector: &metav1.LabelSelector{}, + StaticScrapeNamespaceSelector: &metav1.LabelSelector{}, + StaticScrapeSelector: &metav1.LabelSelector{}, + ProbeNamespaceSelector: &metav1.LabelSelector{}, + ProbeSelector: &metav1.LabelSelector{}, + }, + }, + }, + predefinedObjects: []runtime.Object{ + &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "default", + }, + }, + &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "kube-system", + }, + }, + &vmv1beta1.VMServiceScrape{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: "test-vms", + }, + Spec: vmv1beta1.VMServiceScrapeSpec{ + Selector: metav1.LabelSelector{}, + JobLabel: "app", + NamespaceSelector: vmv1beta1.NamespaceSelector{}, + Endpoints: []vmv1beta1.Endpoint{ + { + EndpointScrapeParams: vmv1beta1.EndpointScrapeParams{ + Path: "/metrics", + }, + Port: "8085", + EndpointAuth: vmv1beta1.EndpointAuth{ + BearerTokenSecret: &corev1.SecretKeySelector{ + Key: "bearer", + LocalObjectReference: corev1.LocalObjectReference{ + Name: "access-creds", + }, + }, + }, + }, + { + EndpointScrapeParams: vmv1beta1.EndpointScrapeParams{ + Path: "/metrics-2", + }, + Port: "8083", + }, + }, + }, + }, + &vmv1beta1.VMProbe{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "kube-system", + Name: "test-vmp", + }, + Spec: vmv1beta1.VMProbeSpec{ + Targets: vmv1beta1.VMProbeTargets{ + StaticConfig: &vmv1beta1.VMProbeTargetStaticConfig{ + Targets: []string{"localhost:8428"}, + }, + }, + VMProberSpec: vmv1beta1.VMProberSpec{URL: "http://blackbox"}, + }, + }, + &vmv1beta1.VMPodScrape{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: "test-vps", + }, + Spec: vmv1beta1.VMPodScrapeSpec{ + JobLabel: "app", + NamespaceSelector: vmv1beta1.NamespaceSelector{}, + Selector: metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{ + { + Key: "app", + Operator: metav1.LabelSelectorOpIn, + Values: []string{"prod"}, + }, + }, + }, + SampleLimit: 10, + PodMetricsEndpoints: []vmv1beta1.PodMetricsEndpoint{ + { + Port: ptr.To("805"), + EndpointScrapeParams: vmv1beta1.EndpointScrapeParams{ + Path: "/metrics-3", + + VMScrapeParams: &vmv1beta1.VMScrapeParams{ + StreamParse: ptr.To(true), + ProxyClientConfig: &vmv1beta1.ProxyAuth{ + TLSConfig: &vmv1beta1.TLSConfig{ + InsecureSkipVerify: true, + KeySecret: &corev1.SecretKeySelector{ + Key: "key", + LocalObjectReference: corev1.LocalObjectReference{ + Name: "access-creds", + }, + }, + Cert: vmv1beta1.SecretOrConfigMap{Secret: &corev1.SecretKeySelector{ + Key: "cert", + LocalObjectReference: corev1.LocalObjectReference{ + Name: "access-creds", + }, + }}, + CA: vmv1beta1.SecretOrConfigMap{ + Secret: &corev1.SecretKeySelector{ + Key: "ca", + LocalObjectReference: corev1.LocalObjectReference{ + Name: "access-creds", + }, + }, + }, + }, + }, + }, + }, + }, + { + Port: ptr.To("801"), + EndpointScrapeParams: vmv1beta1.EndpointScrapeParams{ + Path: "/metrics-5", + }, + EndpointAuth: vmv1beta1.EndpointAuth{ + TLSConfig: &vmv1beta1.TLSConfig{ + InsecureSkipVerify: true, + KeySecret: &corev1.SecretKeySelector{ + Key: "key", + LocalObjectReference: corev1.LocalObjectReference{ + Name: "access-creds", + }, + }, + Cert: vmv1beta1.SecretOrConfigMap{Secret: &corev1.SecretKeySelector{ + Key: "cert", + LocalObjectReference: corev1.LocalObjectReference{ + Name: "access-creds", + }, + }}, + CA: vmv1beta1.SecretOrConfigMap{ + Secret: &corev1.SecretKeySelector{ + Key: "ca", + LocalObjectReference: corev1.LocalObjectReference{ + Name: "access-creds", + }, + }, + }, + }, + }, + }, + }, + }, + }, + &vmv1beta1.VMNodeScrape{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: "test-vms", + }, + Spec: vmv1beta1.VMNodeScrapeSpec{ + EndpointAuth: vmv1beta1.EndpointAuth{ + BasicAuth: &vmv1beta1.BasicAuth{ + Username: corev1.SecretKeySelector{ + Key: "username", + LocalObjectReference: corev1.LocalObjectReference{ + Name: "access-creds", + }, + }, + Password: corev1.SecretKeySelector{ + Key: "password", + LocalObjectReference: corev1.LocalObjectReference{ + Name: "access-creds", + }, + }, + }, + }, + }, + }, + &vmv1beta1.VMStaticScrape{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: "test-vmstatic", + }, + Spec: vmv1beta1.VMStaticScrapeSpec{ + TargetEndpoints: []*vmv1beta1.TargetEndpoint{ + { + EndpointScrapeParams: vmv1beta1.EndpointScrapeParams{ + Path: "/metrics-3", + Scheme: "https", + ProxyURL: ptr.To("https://some-proxy-1"), + }, + EndpointAuth: vmv1beta1.EndpointAuth{ + OAuth2: &vmv1beta1.OAuth2{ + TokenURL: "https://some-tr", + ClientSecret: &corev1.SecretKeySelector{ + Key: "cs", + LocalObjectReference: corev1.LocalObjectReference{ + Name: "access-creds", + }, + }, + ClientID: vmv1beta1.SecretOrConfigMap{ + Secret: &corev1.SecretKeySelector{ + Key: "cid", + LocalObjectReference: corev1.LocalObjectReference{ + Name: "access-creds", + }, + }, + }, + }, + }, + }, + }, + }, + }, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "access-creds", + Namespace: "default", + }, + Data: map[string][]byte{ + "cid": []byte(`some-client-id`), + "cs": []byte(`some-client-secret`), + "username": []byte(`some-username`), + "password": []byte(`some-password`), + "ca": []byte(`some-ca-cert`), + "cert": []byte(`some-cert`), + "key": []byte(`some-key`), + "bearer": []byte(`some-bearer`), + }, + }, + }, + wantConfig: `global: + scrape_interval: 30s + external_labels: + prometheus: default/test +scrape_configs: +- job_name: serviceScrape/default/test-vms/0 + kubernetes_sd_configs: + - role: endpoints + namespaces: + names: + - default + honor_labels: false + metrics_path: /metrics + relabel_configs: + - action: keep + source_labels: + - __meta_kubernetes_endpoint_port_name + regex: "8085" + - source_labels: + - __meta_kubernetes_endpoint_address_target_kind + - __meta_kubernetes_endpoint_address_target_name + separator: ; + regex: Node;(.*) + replacement: ${1} + target_label: node + - source_labels: + - __meta_kubernetes_endpoint_address_target_kind + - __meta_kubernetes_endpoint_address_target_name + separator: ; + regex: Pod;(.*) + replacement: ${1} + target_label: pod + - source_labels: + - __meta_kubernetes_pod_name + target_label: pod + - source_labels: + - __meta_kubernetes_pod_container_name + target_label: container + - source_labels: + - __meta_kubernetes_namespace + target_label: namespace + - source_labels: + - __meta_kubernetes_service_name + target_label: service + - source_labels: + - __meta_kubernetes_service_name + target_label: job + replacement: ${1} + - source_labels: + - __meta_kubernetes_service_label_app + target_label: job + regex: (.+) + replacement: ${1} + - target_label: endpoint + replacement: "8085" + bearer_token: some-bearer +- job_name: serviceScrape/default/test-vms/1 + kubernetes_sd_configs: + - role: endpoints + namespaces: + names: + - default + honor_labels: false + metrics_path: /metrics-2 + relabel_configs: + - action: keep + source_labels: + - __meta_kubernetes_endpoint_port_name + regex: "8083" + - source_labels: + - __meta_kubernetes_endpoint_address_target_kind + - __meta_kubernetes_endpoint_address_target_name + separator: ; + regex: Node;(.*) + replacement: ${1} + target_label: node + - source_labels: + - __meta_kubernetes_endpoint_address_target_kind + - __meta_kubernetes_endpoint_address_target_name + separator: ; + regex: Pod;(.*) + replacement: ${1} + target_label: pod + - source_labels: + - __meta_kubernetes_pod_name + target_label: pod + - source_labels: + - __meta_kubernetes_pod_container_name + target_label: container + - source_labels: + - __meta_kubernetes_namespace + target_label: namespace + - source_labels: + - __meta_kubernetes_service_name + target_label: service + - source_labels: + - __meta_kubernetes_service_name + target_label: job + replacement: ${1} + - source_labels: + - __meta_kubernetes_service_label_app + target_label: job + regex: (.+) + replacement: ${1} + - target_label: endpoint + replacement: "8083" +- job_name: podScrape/default/test-vps/0 + kubernetes_sd_configs: + - role: pod + namespaces: + names: + - default + honor_labels: false + metrics_path: /metrics-3 + sample_limit: 10 + relabel_configs: + - action: drop + source_labels: + - __meta_kubernetes_pod_phase + regex: (Failed|Succeeded) + - action: keep + source_labels: + - __meta_kubernetes_pod_label_app + regex: prod + - action: keep + source_labels: + - __meta_kubernetes_pod_container_port_name + regex: "805" + - source_labels: + - __meta_kubernetes_namespace + target_label: namespace + - source_labels: + - __meta_kubernetes_pod_container_name + target_label: container + - source_labels: + - __meta_kubernetes_pod_name + target_label: pod + - target_label: job + replacement: default/test-vps + - source_labels: + - __meta_kubernetes_pod_label_app + target_label: job + regex: (.+) + replacement: ${1} + - target_label: endpoint + replacement: "805" + stream_parse: true + proxy_tls_config: + insecure_skip_verify: true + ca_file: /etc/vm-tls/certs/default_access-creds_ca + cert_file: /etc/vm-tls/certs/default_access-creds_cert + key_file: /etc/vm-tls/certs/default_access-creds_key +- job_name: podScrape/default/test-vps/1 + kubernetes_sd_configs: + - role: pod + namespaces: + names: + - default + honor_labels: false + metrics_path: /metrics-5 + sample_limit: 10 + relabel_configs: + - action: drop + source_labels: + - __meta_kubernetes_pod_phase + regex: (Failed|Succeeded) + - action: keep + source_labels: + - __meta_kubernetes_pod_label_app + regex: prod + - action: keep + source_labels: + - __meta_kubernetes_pod_container_port_name + regex: "801" + - source_labels: + - __meta_kubernetes_namespace + target_label: namespace + - source_labels: + - __meta_kubernetes_pod_container_name + target_label: container + - source_labels: + - __meta_kubernetes_pod_name + target_label: pod + - target_label: job + replacement: default/test-vps + - source_labels: + - __meta_kubernetes_pod_label_app + target_label: job + regex: (.+) + replacement: ${1} + - target_label: endpoint + replacement: "801" + tls_config: + insecure_skip_verify: true + ca_file: /etc/vm-tls/certs/default_access-creds_ca + cert_file: /etc/vm-tls/certs/default_access-creds_cert + key_file: /etc/vm-tls/certs/default_access-creds_key +- job_name: probe/kube-system/test-vmp + honor_labels: false + metrics_path: /probe + static_configs: + - targets: + - localhost:8428 + relabel_configs: + - source_labels: + - __address__ + target_label: __param_target + - source_labels: + - __param_target + target_label: instance + - target_label: __address__ + replacement: http://blackbox +- job_name: nodeScrape/default/test-vms + kubernetes_sd_configs: + - role: node + honor_labels: false + relabel_configs: + - source_labels: + - __meta_kubernetes_node_name + target_label: node + - target_label: job + replacement: default/test-vms + basic_auth: + username: some-username + password: some-password +- job_name: staticScrape/default/test-vmstatic/0 + static_configs: + - targets: [] + honor_labels: false + metrics_path: /metrics-3 + proxy_url: https://some-proxy-1 + scheme: https + relabel_configs: [] + oauth2: + client_id: some-client-id + client_secret: some-client-secret + token_url: https://some-tr +`, + }) + + // with missing secret references + f(opts{ + cr: &vmv1beta1.VMSingle{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "default", + }, + Spec: vmv1beta1.VMSingleSpec{ + CommonScrapeParams: vmv1beta1.CommonScrapeParams{ + IngestOnlyMode: ptr.To(false), + ServiceScrapeNamespaceSelector: &metav1.LabelSelector{}, + ServiceScrapeSelector: &metav1.LabelSelector{}, + PodScrapeSelector: &metav1.LabelSelector{}, + PodScrapeNamespaceSelector: &metav1.LabelSelector{}, + NodeScrapeNamespaceSelector: &metav1.LabelSelector{}, + NodeScrapeSelector: &metav1.LabelSelector{}, + StaticScrapeNamespaceSelector: &metav1.LabelSelector{}, + StaticScrapeSelector: &metav1.LabelSelector{}, + ProbeNamespaceSelector: &metav1.LabelSelector{}, + ProbeSelector: &metav1.LabelSelector{}, + }, + }, + }, + predefinedObjects: []runtime.Object{ + &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "default", + }, + }, + + &vmv1beta1.VMNodeScrape{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: "test-bad-0", + }, + Spec: vmv1beta1.VMNodeScrapeSpec{ + EndpointAuth: vmv1beta1.EndpointAuth{ + BasicAuth: &vmv1beta1.BasicAuth{ + Username: corev1.SecretKeySelector{ + Key: "username", + LocalObjectReference: corev1.LocalObjectReference{ + Name: "access-credentials", + }, + }, + Password: corev1.SecretKeySelector{ + Key: "password", + LocalObjectReference: corev1.LocalObjectReference{ + Name: "access-credentials", + }, + }, + }, + }, + }, + }, + + &vmv1beta1.VMNodeScrape{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: "test-good", + }, + Spec: vmv1beta1.VMNodeScrapeSpec{}, + }, + + &vmv1beta1.VMNodeScrape{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: "bad-1", + }, + Spec: vmv1beta1.VMNodeScrapeSpec{ + EndpointAuth: vmv1beta1.EndpointAuth{ + BearerTokenSecret: &corev1.SecretKeySelector{ + Key: "username", + LocalObjectReference: corev1.LocalObjectReference{ + Name: "access-credentials", + }, + }, + }, + }, + }, + &vmv1beta1.VMPodScrape{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: "test-vps-mixed", + }, + Spec: vmv1beta1.VMPodScrapeSpec{ + JobLabel: "app", + NamespaceSelector: vmv1beta1.NamespaceSelector{}, + Selector: metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{ + { + Key: "app", + Operator: metav1.LabelSelectorOpIn, + Values: []string{"prod"}, + }, + }, + }, + SampleLimit: 10, + PodMetricsEndpoints: []vmv1beta1.PodMetricsEndpoint{ + { + Port: ptr.To("805"), + EndpointScrapeParams: vmv1beta1.EndpointScrapeParams{ + Path: "/metrics-3", + VMScrapeParams: &vmv1beta1.VMScrapeParams{ + StreamParse: ptr.To(true), + ProxyClientConfig: &vmv1beta1.ProxyAuth{ + BearerToken: &corev1.SecretKeySelector{ + Key: "username", + LocalObjectReference: corev1.LocalObjectReference{ + Name: "access-credentials", + }, + }, + }, + }, + }, + }, + { + Port: ptr.To("801"), + EndpointScrapeParams: vmv1beta1.EndpointScrapeParams{ + Path: "/metrics-5", + }, + EndpointAuth: vmv1beta1.EndpointAuth{ + BasicAuth: &vmv1beta1.BasicAuth{ + Username: corev1.SecretKeySelector{ + Key: "username", + LocalObjectReference: corev1.LocalObjectReference{ + Name: "access-credentials", + }, + }, + Password: corev1.SecretKeySelector{ + Key: "password", + LocalObjectReference: corev1.LocalObjectReference{ + Name: "access-credentials", + }, + }, + }, + }, + }, + { + Port: ptr.To("801"), + EndpointScrapeParams: vmv1beta1.EndpointScrapeParams{ + Path: "/metrics-5-good", + }, + }, + }, + }, + }, + &vmv1beta1.VMPodScrape{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: "test-vps-good", + }, + Spec: vmv1beta1.VMPodScrapeSpec{ + JobLabel: "app", + NamespaceSelector: vmv1beta1.NamespaceSelector{}, + Selector: metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{ + { + Key: "app", + Operator: metav1.LabelSelectorOpIn, + Values: []string{"prod"}, + }, + }, + }, + PodMetricsEndpoints: []vmv1beta1.PodMetricsEndpoint{ + { + Port: ptr.To("8011"), + EndpointScrapeParams: vmv1beta1.EndpointScrapeParams{ + Path: "/metrics-1-good", + }, + }, + }, + }, + }, + &vmv1beta1.VMStaticScrape{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: "test-vmstatic-bad", + }, + Spec: vmv1beta1.VMStaticScrapeSpec{ + TargetEndpoints: []*vmv1beta1.TargetEndpoint{ + { + EndpointScrapeParams: vmv1beta1.EndpointScrapeParams{ + Path: "/metrics-3", + Scheme: "https", + ProxyURL: ptr.To("https://some-proxy-1"), + }, + EndpointAuth: vmv1beta1.EndpointAuth{ + OAuth2: &vmv1beta1.OAuth2{ + TokenURL: "https://some-tr", + ClientSecret: &corev1.SecretKeySelector{ + Key: "cs", + LocalObjectReference: corev1.LocalObjectReference{ + Name: "access-credentials", + }, + }, + ClientID: vmv1beta1.SecretOrConfigMap{ + Secret: &corev1.SecretKeySelector{ + Key: "cid", + LocalObjectReference: corev1.LocalObjectReference{ + Name: "access-credentials", + }, + }, + }, + }, + }, + }, + }, + }, + }, + &vmv1beta1.VMStaticScrape{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: "test-vmstatic-bad-tls", + }, + Spec: vmv1beta1.VMStaticScrapeSpec{ + TargetEndpoints: []*vmv1beta1.TargetEndpoint{ + { + EndpointScrapeParams: vmv1beta1.EndpointScrapeParams{ + Path: "/metrics-3", + Scheme: "https", + ProxyURL: ptr.To("https://some-proxy-1"), + }, + EndpointAuth: vmv1beta1.EndpointAuth{ + TLSConfig: &vmv1beta1.TLSConfig{ + Cert: vmv1beta1.SecretOrConfigMap{ + Secret: &corev1.SecretKeySelector{ + Key: "cert", + LocalObjectReference: corev1.LocalObjectReference{ + Name: "tls-credentials", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + wantConfig: `global: + scrape_interval: 30s + external_labels: + prometheus: default/test +scrape_configs: +- job_name: podScrape/default/test-vps-good/0 + kubernetes_sd_configs: + - role: pod + namespaces: + names: + - default + honor_labels: false + metrics_path: /metrics-1-good + relabel_configs: + - action: drop + source_labels: + - __meta_kubernetes_pod_phase + regex: (Failed|Succeeded) + - action: keep + source_labels: + - __meta_kubernetes_pod_label_app + regex: prod + - action: keep + source_labels: + - __meta_kubernetes_pod_container_port_name + regex: "8011" + - source_labels: + - __meta_kubernetes_namespace + target_label: namespace + - source_labels: + - __meta_kubernetes_pod_container_name + target_label: container + - source_labels: + - __meta_kubernetes_pod_name + target_label: pod + - target_label: job + replacement: default/test-vps-good + - source_labels: + - __meta_kubernetes_pod_label_app + target_label: job + regex: (.+) + replacement: ${1} + - target_label: endpoint + replacement: "8011" +- job_name: nodeScrape/default/test-good + kubernetes_sd_configs: + - role: node + honor_labels: false + relabel_configs: + - source_labels: + - __meta_kubernetes_node_name + target_label: node + - target_label: job + replacement: default/test-good +`, + }) + + // with changed default config value + f(opts{ + cr: &vmv1beta1.VMSingle{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "default", + }, + Spec: vmv1beta1.VMSingleSpec{ + CommonScrapeParams: vmv1beta1.CommonScrapeParams{ + IngestOnlyMode: ptr.To(false), + ServiceScrapeNamespaceSelector: &metav1.LabelSelector{}, + ServiceScrapeSelector: &metav1.LabelSelector{}, + PodScrapeSelector: &metav1.LabelSelector{}, + PodScrapeNamespaceSelector: &metav1.LabelSelector{}, + NodeScrapeNamespaceSelector: &metav1.LabelSelector{}, + NodeScrapeSelector: &metav1.LabelSelector{}, + StaticScrapeNamespaceSelector: &metav1.LabelSelector{}, + StaticScrapeSelector: &metav1.LabelSelector{}, + ProbeNamespaceSelector: &metav1.LabelSelector{}, + ProbeSelector: &metav1.LabelSelector{}, + ScrapeTimeout: "20m", + ScrapeInterval: "30m", + ExternalLabels: map[string]string{ + "externalLabelName": "externalLabelValue", + }, + GlobalScrapeRelabelConfigs: []*vmv1beta1.RelabelConfig{ + { + UnderScoreSourceLabels: []string{"test2"}, + }, + }, + GlobalScrapeMetricRelabelConfigs: []*vmv1beta1.RelabelConfig{ + { + UnderScoreSourceLabels: []string{"test1"}, + }, + }, + }, + }, + }, + cfgMutator: func(c *config.BaseOperatorConf) { + c.VMServiceScrapeDefault.EnforceEndpointSlices = true + }, + predefinedObjects: []runtime.Object{ + &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "default", + }, + }, + &vmv1beta1.VMServiceScrape{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: "test-vms", + }, + Spec: vmv1beta1.VMServiceScrapeSpec{ + Selector: metav1.LabelSelector{}, + JobLabel: "app", + NamespaceSelector: vmv1beta1.NamespaceSelector{}, + Endpoints: []vmv1beta1.Endpoint{ + { + EndpointScrapeParams: vmv1beta1.EndpointScrapeParams{ + Path: "/metrics", + }, + Port: "8085", + EndpointAuth: vmv1beta1.EndpointAuth{ + BearerTokenSecret: &corev1.SecretKeySelector{ + Key: "bearer", + LocalObjectReference: corev1.LocalObjectReference{ + Name: "access-creds", + }, + }, + }, + }, + { + EndpointScrapeParams: vmv1beta1.EndpointScrapeParams{ + Path: "/metrics-2", + }, + Port: "8083", + }, + }, + }, + }, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "access-creds", + Namespace: "default", + }, + Data: map[string][]byte{ + "cid": []byte(`some-client-id`), + "cs": []byte(`some-client-secret`), + "username": []byte(`some-username`), + "password": []byte(`some-password`), + "ca": []byte(`some-ca-cert`), + "cert": []byte(`some-cert`), + "key": []byte(`some-key`), + "bearer": []byte(`some-bearer`), + }, + }, + }, + wantConfig: `global: + scrape_interval: 30m + external_labels: + externalLabelName: externalLabelValue + prometheus: default/test + scrape_timeout: 20m + metric_relabel_configs: + - source_labels: + - test1 + relabel_configs: + - source_labels: + - test2 +scrape_configs: +- job_name: serviceScrape/default/test-vms/0 + kubernetes_sd_configs: + - role: endpointslice + namespaces: + names: + - default + honor_labels: false + metrics_path: /metrics + relabel_configs: + - action: keep + source_labels: + - __meta_kubernetes_endpointslice_port_name + regex: "8085" + - source_labels: + - __meta_kubernetes_endpointslice_address_target_kind + - __meta_kubernetes_endpointslice_address_target_name + separator: ; + regex: Node;(.*) + replacement: ${1} + target_label: node + - source_labels: + - __meta_kubernetes_endpointslice_address_target_kind + - __meta_kubernetes_endpointslice_address_target_name + separator: ; + regex: Pod;(.*) + replacement: ${1} + target_label: pod + - source_labels: + - __meta_kubernetes_pod_name + target_label: pod + - source_labels: + - __meta_kubernetes_pod_container_name + target_label: container + - source_labels: + - __meta_kubernetes_namespace + target_label: namespace + - source_labels: + - __meta_kubernetes_service_name + target_label: service + - source_labels: + - __meta_kubernetes_service_name + target_label: job + replacement: ${1} + - source_labels: + - __meta_kubernetes_service_label_app + target_label: job + regex: (.+) + replacement: ${1} + - target_label: endpoint + replacement: "8085" + bearer_token: some-bearer +- job_name: serviceScrape/default/test-vms/1 + kubernetes_sd_configs: + - role: endpointslice + namespaces: + names: + - default + honor_labels: false + metrics_path: /metrics-2 + relabel_configs: + - action: keep + source_labels: + - __meta_kubernetes_endpointslice_port_name + regex: "8083" + - source_labels: + - __meta_kubernetes_endpointslice_address_target_kind + - __meta_kubernetes_endpointslice_address_target_name + separator: ; + regex: Node;(.*) + replacement: ${1} + target_label: node + - source_labels: + - __meta_kubernetes_endpointslice_address_target_kind + - __meta_kubernetes_endpointslice_address_target_name + separator: ; + regex: Pod;(.*) + replacement: ${1} + target_label: pod + - source_labels: + - __meta_kubernetes_pod_name + target_label: pod + - source_labels: + - __meta_kubernetes_pod_container_name + target_label: container + - source_labels: + - __meta_kubernetes_namespace + target_label: namespace + - source_labels: + - __meta_kubernetes_service_name + target_label: service + - source_labels: + - __meta_kubernetes_service_name + target_label: job + replacement: ${1} + - source_labels: + - __meta_kubernetes_service_label_app + target_label: job + regex: (.+) + replacement: ${1} + - target_label: endpoint + replacement: "8083" +`, + }) + + // with oauth2 tls config + f(opts{ + cr: &vmv1beta1.VMSingle{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "default", + }, + Spec: vmv1beta1.VMSingleSpec{ + CommonScrapeParams: vmv1beta1.CommonScrapeParams{ + IngestOnlyMode: ptr.To(false), + ServiceScrapeNamespaceSelector: &metav1.LabelSelector{}, + ServiceScrapeSelector: &metav1.LabelSelector{}, + PodScrapeSelector: &metav1.LabelSelector{}, + PodScrapeNamespaceSelector: &metav1.LabelSelector{}, + NodeScrapeNamespaceSelector: &metav1.LabelSelector{}, + NodeScrapeSelector: &metav1.LabelSelector{}, + StaticScrapeNamespaceSelector: &metav1.LabelSelector{}, + StaticScrapeSelector: &metav1.LabelSelector{}, + ProbeNamespaceSelector: &metav1.LabelSelector{}, + ProbeSelector: &metav1.LabelSelector{}, + }, + }, + }, + predefinedObjects: []runtime.Object{ + &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "default", + }, + }, + &vmv1beta1.VMServiceScrape{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: "test-vms", + }, + Spec: vmv1beta1.VMServiceScrapeSpec{ + Selector: metav1.LabelSelector{}, + JobLabel: "app", + NamespaceSelector: vmv1beta1.NamespaceSelector{}, + Endpoints: []vmv1beta1.Endpoint{ + { + EndpointScrapeParams: vmv1beta1.EndpointScrapeParams{ + Path: "/metrics", + }, + Port: "8085", + EndpointAuth: vmv1beta1.EndpointAuth{ + OAuth2: &vmv1beta1.OAuth2{ + ClientID: vmv1beta1.SecretOrConfigMap{ + Secret: &corev1.SecretKeySelector{ + Key: "CLIENT_ID", + LocalObjectReference: corev1.LocalObjectReference{ + Name: "oauth2-access", + }, + }, + }, + ClientSecret: &corev1.SecretKeySelector{ + Key: "CLIENT_SECRET", + LocalObjectReference: corev1.LocalObjectReference{ + Name: "oauth2-access", + }, + }, + TokenURL: "http://some-url", + TLSConfig: &vmv1beta1.TLSConfig{ + CA: vmv1beta1.SecretOrConfigMap{ + ConfigMap: &corev1.ConfigMapKeySelector{ + Key: "CA", + LocalObjectReference: corev1.LocalObjectReference{ + Name: "tls-default", + }, + }, + }, + Cert: vmv1beta1.SecretOrConfigMap{ + Secret: &corev1.SecretKeySelector{ + Key: "CERT", + LocalObjectReference: corev1.LocalObjectReference{ + Name: "tls-auth", + }, + }, + }, + KeySecret: &corev1.SecretKeySelector{ + Key: "SECRET_KEY", + LocalObjectReference: corev1.LocalObjectReference{ + Name: "tls-auth", + }, + }, + InsecureSkipVerify: false, + }, + }, + BearerTokenSecret: &corev1.SecretKeySelector{ + Key: "bearer", + LocalObjectReference: corev1.LocalObjectReference{ + Name: "access-creds", + }, + }, + }, + }, + }, + }, + }, + &vmv1beta1.VMPodScrape{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: "dev-pods", + }, + Spec: vmv1beta1.VMPodScrapeSpec{ + PodMetricsEndpoints: []vmv1beta1.PodMetricsEndpoint{ + { + Port: ptr.To("8081"), + EndpointAuth: vmv1beta1.EndpointAuth{ + OAuth2: &vmv1beta1.OAuth2{ + ClientID: vmv1beta1.SecretOrConfigMap{ + Secret: &corev1.SecretKeySelector{ + Key: "CLIENT_ID", + LocalObjectReference: corev1.LocalObjectReference{ + Name: "oauth2-access", + }, + }, + }, + ClientSecret: &corev1.SecretKeySelector{ + Key: "CLIENT_SECRET", + LocalObjectReference: corev1.LocalObjectReference{ + Name: "oauth2-access", + }, + }, + TokenURL: "http://some-url", + TLSConfig: &vmv1beta1.TLSConfig{ + CA: vmv1beta1.SecretOrConfigMap{ + ConfigMap: &corev1.ConfigMapKeySelector{ + Key: "CA", + LocalObjectReference: corev1.LocalObjectReference{ + Name: "tls-default", + }, + }, + }, + Cert: vmv1beta1.SecretOrConfigMap{ + Secret: &corev1.SecretKeySelector{ + Key: "CERT", + LocalObjectReference: corev1.LocalObjectReference{ + Name: "tls-auth", + }, + }, + }, + KeySecret: &corev1.SecretKeySelector{ + Key: "SECRET_KEY", + LocalObjectReference: corev1.LocalObjectReference{ + Name: "tls-auth", + }, + }, + InsecureSkipVerify: false, + }, + }, + }, + }, + }, + }, + }, + &vmv1beta1.VMNodeScrape{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: "k8s-nodes", + }, + Spec: vmv1beta1.VMNodeScrapeSpec{ + Port: "9093", + EndpointAuth: vmv1beta1.EndpointAuth{ + OAuth2: &vmv1beta1.OAuth2{ + ClientID: vmv1beta1.SecretOrConfigMap{ + Secret: &corev1.SecretKeySelector{ + Key: "CLIENT_ID", + LocalObjectReference: corev1.LocalObjectReference{ + Name: "oauth2-access", + }, + }, + }, + ClientSecret: &corev1.SecretKeySelector{ + Key: "CLIENT_SECRET", + LocalObjectReference: corev1.LocalObjectReference{ + Name: "oauth2-access", + }, + }, + TokenURL: "http://some-url", + TLSConfig: &vmv1beta1.TLSConfig{ + CA: vmv1beta1.SecretOrConfigMap{ + ConfigMap: &corev1.ConfigMapKeySelector{ + Key: "CA", + LocalObjectReference: corev1.LocalObjectReference{ + Name: "tls-default", + }, + }, + }, + Cert: vmv1beta1.SecretOrConfigMap{ + Secret: &corev1.SecretKeySelector{ + Key: "CERT", + LocalObjectReference: corev1.LocalObjectReference{ + Name: "tls-auth", + }, + }, + }, + KeySecret: &corev1.SecretKeySelector{ + Key: "SECRET_KEY", + LocalObjectReference: corev1.LocalObjectReference{ + Name: "tls-auth", + }, + }, + InsecureSkipVerify: false, + }, + }, + }, + }, + }, + &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "tls-default", + Namespace: "default", + }, + Data: map[string]string{ + "CA": "ca data", + }, + }, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "tls-auth", + Namespace: "default", + }, + Data: map[string][]byte{ + "CERT": []byte(`cert data`), + "SECRET_KEY": []byte(`key data`), + }, + }, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "oauth2-access", + Namespace: "default", + }, + Data: map[string][]byte{ + "CLIENT_ID": []byte(`data`), + "CLIENT_SECRET": []byte(`data`), + }, + }, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "access-creds", + Namespace: "default", + }, + Data: map[string][]byte{ + "cid": []byte(`some-client-id`), + "cs": []byte(`some-client-secret`), + "username": []byte(`some-username`), + "password": []byte(`some-password`), + "ca": []byte(`some-ca-cert`), + "cert": []byte(`some-cert`), + "key": []byte(`some-key`), + "bearer": []byte(`some-bearer`), + }, + }, + }, + wantConfig: `global: + scrape_interval: 30s + external_labels: + prometheus: default/test +scrape_configs: +- job_name: serviceScrape/default/test-vms/0 + kubernetes_sd_configs: + - role: endpoints + namespaces: + names: + - default + honor_labels: false + metrics_path: /metrics + relabel_configs: + - action: keep + source_labels: + - __meta_kubernetes_endpoint_port_name + regex: "8085" + - source_labels: + - __meta_kubernetes_endpoint_address_target_kind + - __meta_kubernetes_endpoint_address_target_name + separator: ; + regex: Node;(.*) + replacement: ${1} + target_label: node + - source_labels: + - __meta_kubernetes_endpoint_address_target_kind + - __meta_kubernetes_endpoint_address_target_name + separator: ; + regex: Pod;(.*) + replacement: ${1} + target_label: pod + - source_labels: + - __meta_kubernetes_pod_name + target_label: pod + - source_labels: + - __meta_kubernetes_pod_container_name + target_label: container + - source_labels: + - __meta_kubernetes_namespace + target_label: namespace + - source_labels: + - __meta_kubernetes_service_name + target_label: service + - source_labels: + - __meta_kubernetes_service_name + target_label: job + replacement: ${1} + - source_labels: + - __meta_kubernetes_service_label_app + target_label: job + regex: (.+) + replacement: ${1} + - target_label: endpoint + replacement: "8085" + bearer_token: some-bearer + oauth2: + client_id: data + client_secret: data + token_url: http://some-url + tls_config: + ca_file: /etc/vm-tls/certs/default_configmap_tls-default_CA + cert_file: /etc/vm-tls/certs/default_tls-auth_CERT + key_file: /etc/vm-tls/certs/default_tls-auth_SECRET_KEY +- job_name: podScrape/default/dev-pods/0 + kubernetes_sd_configs: + - role: pod + namespaces: + names: + - default + honor_labels: false + relabel_configs: + - action: drop + source_labels: + - __meta_kubernetes_pod_phase + regex: (Failed|Succeeded) + - action: keep + source_labels: + - __meta_kubernetes_pod_container_port_name + regex: "8081" + - source_labels: + - __meta_kubernetes_namespace + target_label: namespace + - source_labels: + - __meta_kubernetes_pod_container_name + target_label: container + - source_labels: + - __meta_kubernetes_pod_name + target_label: pod + - target_label: job + replacement: default/dev-pods + - target_label: endpoint + replacement: "8081" + oauth2: + client_id: data + client_secret: data + token_url: http://some-url + tls_config: + ca_file: /etc/vm-tls/certs/default_configmap_tls-default_CA + cert_file: /etc/vm-tls/certs/default_tls-auth_CERT + key_file: /etc/vm-tls/certs/default_tls-auth_SECRET_KEY +- job_name: nodeScrape/default/k8s-nodes + kubernetes_sd_configs: + - role: node + honor_labels: false + relabel_configs: + - source_labels: + - __meta_kubernetes_node_name + target_label: node + - target_label: job + replacement: default/k8s-nodes + - source_labels: + - __address__ + target_label: __address__ + regex: ^(.*):(.*) + replacement: ${1}:9093 + oauth2: + client_id: data + client_secret: data + token_url: http://some-url + tls_config: + ca_file: /etc/vm-tls/certs/default_configmap_tls-default_CA + cert_file: /etc/vm-tls/certs/default_tls-auth_CERT + key_file: /etc/vm-tls/certs/default_tls-auth_SECRET_KEY +`, + }) + + // with invalid objects syntax + f(opts{ + cr: &vmv1beta1.VMSingle{ + ObjectMeta: metav1.ObjectMeta{ + Name: "select-all", + Namespace: "default", + }, + Spec: vmv1beta1.VMSingleSpec{ + CommonScrapeParams: vmv1beta1.CommonScrapeParams{ + IngestOnlyMode: ptr.To(false), + SelectAllByDefault: true, + CommonScrapeSecurityEnforcements: vmv1beta1.CommonScrapeSecurityEnforcements{ + ArbitraryFSAccessThroughSMs: vmv1beta1.ArbitraryFSAccessThroughSMsConfig{ + Deny: true, + }, + }, + }, + }, + }, + predefinedObjects: []runtime.Object{ + &vmv1beta1.VMServiceScrape{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: "fs-access", + }, + Spec: vmv1beta1.VMServiceScrapeSpec{ + Endpoints: []vmv1beta1.Endpoint{ + { + Port: "8080", + EndpointAuth: vmv1beta1.EndpointAuth{ + TLSConfig: &vmv1beta1.TLSConfig{ + CAFile: "/etc/passwd", + }, + }, + }, + }, + }, + }, + &vmv1beta1.VMPodScrape{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: "bad-syntax", + }, + Spec: vmv1beta1.VMPodScrapeSpec{ + Selector: *metav1.SetAsLabelSelector(map[string]string{ + "alb.ingress.kubernetes.io/tags": "Environment=devl", + }), + }, + }, + &vmv1beta1.VMProbe{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: "bad-syntax", + }, + Spec: vmv1beta1.VMProbeSpec{ + Targets: vmv1beta1.VMProbeTargets{ + Ingress: &vmv1beta1.ProbeTargetIngress{ + Selector: *metav1.SetAsLabelSelector(map[string]string{ + "alb.ingress.kubernetes.io/tags": "Environment=devl", + }), + }, + }, + }, + }, + }, + wantConfig: `global: + scrape_interval: 30s + external_labels: + prometheus: default/select-all +scrape_configs: [] +`, + }) + + // with partial missing refs + f(opts{ + cr: &vmv1beta1.VMSingle{ + ObjectMeta: metav1.ObjectMeta{ + Name: "select-all", + Namespace: "default", + }, + Spec: vmv1beta1.VMSingleSpec{ + CommonScrapeParams: vmv1beta1.CommonScrapeParams{ + IngestOnlyMode: ptr.To(false), + SelectAllByDefault: true, + }, + }, + }, + predefinedObjects: []runtime.Object{ + &vmv1beta1.VMServiceScrape{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: "partially-correct", + }, + Spec: vmv1beta1.VMServiceScrapeSpec{ + Endpoints: []vmv1beta1.Endpoint{ + { + Port: "8080", + EndpointAuth: vmv1beta1.EndpointAuth{ + TLSConfig: &vmv1beta1.TLSConfig{ + CA: vmv1beta1.SecretOrConfigMap{ + Secret: &corev1.SecretKeySelector{ + Key: "ca", + LocalObjectReference: corev1.LocalObjectReference{ + Name: "tls-auth", + }, + }, + }, + }, + }, + }, + { + Port: "8081", + }, + }, + }, + }, + &vmv1beta1.VMPodScrape{ + ObjectMeta: metav1.ObjectMeta{ + Name: "partially-correct", + Namespace: "default", + }, + Spec: vmv1beta1.VMPodScrapeSpec{ + PodMetricsEndpoints: []vmv1beta1.PodMetricsEndpoint{ + { + Port: ptr.To("8035"), + }, + { + Port: ptr.To("8080"), + EndpointAuth: vmv1beta1.EndpointAuth{ + TLSConfig: &vmv1beta1.TLSConfig{ + CA: vmv1beta1.SecretOrConfigMap{ + Secret: &corev1.SecretKeySelector{ + Key: "ca", + LocalObjectReference: corev1.LocalObjectReference{ + Name: "tls-auth", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + wantConfig: `global: + scrape_interval: 30s + external_labels: + prometheus: default/select-all +scrape_configs: [] +`, + }) + + // with scrape classes + f(opts{ + + cr: &vmv1beta1.VMSingle{ + ObjectMeta: metav1.ObjectMeta{ + Name: "scrape-classes", + Namespace: "default", + }, + Spec: vmv1beta1.VMSingleSpec{ + CommonScrapeParams: vmv1beta1.CommonScrapeParams{ + IngestOnlyMode: ptr.To(false), + SelectAllByDefault: true, + ScrapeClasses: []vmv1beta1.ScrapeClass{ + { + Name: "default", + Default: ptr.To(true), + EndpointAuth: vmv1beta1.EndpointAuth{ + TLSConfig: &vmv1beta1.TLSConfig{ + CA: vmv1beta1.SecretOrConfigMap{ConfigMap: &corev1.ConfigMapKeySelector{Key: "CA", LocalObjectReference: corev1.LocalObjectReference{Name: "tls-default"}}}, + ServerName: "my-server", + }, + }, + AttachMetadata: &vmv1beta1.AttachMetadata{Node: ptr.To(true)}, + EndpointRelabelings: vmv1beta1.EndpointRelabelings{ + MetricRelabelConfigs: []*vmv1beta1.RelabelConfig{}, + RelabelConfigs: []*vmv1beta1.RelabelConfig{}, + }, + }, + + { + Name: "with-oauth2", + EndpointAuth: vmv1beta1.EndpointAuth{ + OAuth2: &vmv1beta1.OAuth2{ + TokenURL: "http://some-other", + ClientSecretFile: "/path/to/file", + ClientID: vmv1beta1.SecretOrConfigMap{ + Secret: &corev1.SecretKeySelector{ + Key: "CLIENT_ID", + LocalObjectReference: corev1.LocalObjectReference{Name: "oauth2-access"}, + }, + }, + }, + }, + }, + { + Name: "with-basic-auth", + EndpointAuth: vmv1beta1.EndpointAuth{ + BasicAuth: &vmv1beta1.BasicAuth{ + Username: corev1.SecretKeySelector{ + Key: "username", + LocalObjectReference: corev1.LocalObjectReference{Name: "basic-auth"}, + }, + PasswordFile: "/path/to/file", + }, + }, + }, + { + Name: "with-oauth2-tls", + EndpointAuth: vmv1beta1.EndpointAuth{ + OAuth2: &vmv1beta1.OAuth2{ + TokenURL: "http://some", + ClientSecretFile: "/path/to/file", + ClientID: vmv1beta1.SecretOrConfigMap{ + Secret: &corev1.SecretKeySelector{ + Key: "CLIENT_ID", + LocalObjectReference: corev1.LocalObjectReference{Name: "oauth2-access"}, + }, + }, + TLSConfig: &vmv1beta1.TLSConfig{ + CA: vmv1beta1.SecretOrConfigMap{ConfigMap: &corev1.ConfigMapKeySelector{Key: "CA", LocalObjectReference: corev1.LocalObjectReference{Name: "tls-default"}}}, + Cert: vmv1beta1.SecretOrConfigMap{Secret: &corev1.SecretKeySelector{Key: "CERT", LocalObjectReference: corev1.LocalObjectReference{Name: "tls-auth"}}}, + KeySecret: &corev1.SecretKeySelector{Key: "CERT", LocalObjectReference: corev1.LocalObjectReference{Name: "tls-auth"}}, + }, + }, + }, + }, + }, + }, + }, + }, + predefinedObjects: []runtime.Object{ + &vmv1beta1.VMPodScrape{ + ObjectMeta: metav1.ObjectMeta{ + Name: "class", + Namespace: "default", + }, + Spec: vmv1beta1.VMPodScrapeSpec{ + ScrapeClassName: ptr.To("default"), + PodMetricsEndpoints: []vmv1beta1.PodMetricsEndpoint{{Port: ptr.To("some")}}, + }, + }, + &vmv1beta1.VMPodScrape{ + ObjectMeta: metav1.ObjectMeta{ + Name: "class-oauth2-tls", + Namespace: "default", + }, + Spec: vmv1beta1.VMPodScrapeSpec{ + ScrapeClassName: ptr.To("with-oauth2-tls"), + PodMetricsEndpoints: []vmv1beta1.PodMetricsEndpoint{{Port: ptr.To("some-other")}}, + }, + }, + &vmv1beta1.VMNodeScrape{ + ObjectMeta: metav1.ObjectMeta{ + Name: "class-oauth2", + Namespace: "default", + }, + Spec: vmv1beta1.VMNodeScrapeSpec{ + ScrapeClassName: ptr.To("with-basic-auth"), + Port: "8035", + }, + }, + &vmv1beta1.VMStaticScrape{ + ObjectMeta: metav1.ObjectMeta{ + Name: "class-oauth2", + Namespace: "default", + }, + Spec: vmv1beta1.VMStaticScrapeSpec{ + ScrapeClassName: ptr.To("with-oauth2"), + TargetEndpoints: []*vmv1beta1.TargetEndpoint{ + { + Targets: []string{"host-1", "host-2"}, + }, + }, + }, + }, + &vmv1beta1.VMScrapeConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: "with-own", + Namespace: "default", + }, + Spec: vmv1beta1.VMScrapeConfigSpec{ + ConsulSDConfigs: []vmv1beta1.ConsulSDConfig{ + { + Server: "some", + TLSConfig: &vmv1beta1.TLSConfig{ + CAFile: "/some/other/path", + CertFile: "/some/other/cert", + KeyFile: "/some/other/key", + ServerName: "my-name", + }, + }, + }, + }, + }, + &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "tls-default", + Namespace: "default", + }, + Data: map[string]string{ + "CA": "ca data", + }, + }, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "tls-auth", + Namespace: "default", + }, + Data: map[string][]byte{ + "CERT": []byte(`cert data`), + "SECRET_KEY": []byte(`key data`), + }, + }, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "oauth2-access", + Namespace: "default", + }, + Data: map[string][]byte{ + "CLIENT_ID": []byte(`data`), + "CLIENT_SECRET": []byte(`data`), + }, + }, + }, + wantConfig: `global: + scrape_interval: 30s + external_labels: + prometheus: default/scrape-classes +scrape_configs: +- job_name: podScrape/default/class/0 + kubernetes_sd_configs: + - role: pod + attach_metadata: + node: true + namespaces: + names: + - default + honor_labels: false + relabel_configs: + - action: drop + source_labels: + - __meta_kubernetes_pod_phase + regex: (Failed|Succeeded) + - action: keep + source_labels: + - __meta_kubernetes_pod_container_port_name + regex: some + - source_labels: + - __meta_kubernetes_namespace + target_label: namespace + - source_labels: + - __meta_kubernetes_pod_container_name + target_label: container + - source_labels: + - __meta_kubernetes_pod_name + target_label: pod + - target_label: job + replacement: default/class + - target_label: endpoint + replacement: some + tls_config: + ca_file: /etc/vm-tls/certs/default_configmap_tls-default_CA + server_name: my-server +- job_name: podScrape/default/class-oauth2-tls/0 + kubernetes_sd_configs: + - role: pod + namespaces: + names: + - default + honor_labels: false + relabel_configs: + - action: drop + source_labels: + - __meta_kubernetes_pod_phase + regex: (Failed|Succeeded) + - action: keep + source_labels: + - __meta_kubernetes_pod_container_port_name + regex: some-other + - source_labels: + - __meta_kubernetes_namespace + target_label: namespace + - source_labels: + - __meta_kubernetes_pod_container_name + target_label: container + - source_labels: + - __meta_kubernetes_pod_name + target_label: pod + - target_label: job + replacement: default/class-oauth2-tls + - target_label: endpoint + replacement: some-other + oauth2: + client_id: data + client_secret_file: /path/to/file + token_url: http://some + tls_config: + ca_file: /etc/vm-tls/certs/default_configmap_tls-default_CA + cert_file: /etc/vm-tls/certs/default_tls-auth_CERT + key_file: /etc/vm-tls/certs/default_tls-auth_CERT +- job_name: staticScrape/default/class-oauth2/0 + static_configs: + - targets: + - host-1 + - host-2 + honor_labels: false + relabel_configs: [] + oauth2: + client_id: data + client_secret_file: /path/to/file + token_url: http://some-other +- job_name: scrapeConfig/default/with-own + honor_labels: false + relabel_configs: [] + tls_config: + ca_file: /etc/vm-tls/certs/default_configmap_tls-default_CA + server_name: my-server + consul_sd_configs: + - server: some + tls_config: + ca_file: /some/other/path + cert_file: /some/other/cert + key_file: /some/other/key + server_name: my-name +`, + }) + + // oauth2 with partial fields set + f(opts{ + cr: &vmv1beta1.VMSingle{ + ObjectMeta: metav1.ObjectMeta{ + Name: "with-oauth2", + Namespace: "default", + }, + Spec: vmv1beta1.VMSingleSpec{ + CommonScrapeParams: vmv1beta1.CommonScrapeParams{ + IngestOnlyMode: ptr.To(false), + SelectAllByDefault: true, + }, + }, + }, + predefinedObjects: []runtime.Object{ + &vmv1beta1.VMPodScrape{ + ObjectMeta: metav1.ObjectMeta{ + Name: "with-oauth2-simple", + Namespace: "default", + }, + Spec: vmv1beta1.VMPodScrapeSpec{ + PodMetricsEndpoints: []vmv1beta1.PodMetricsEndpoint{ + { + Port: ptr.To("8085"), + EndpointAuth: vmv1beta1.EndpointAuth{ + OAuth2: &vmv1beta1.OAuth2{ + TokenURL: "http://some-url", + }, + }, + }, + { + Port: ptr.To("8085"), + EndpointAuth: vmv1beta1.EndpointAuth{ + OAuth2: &vmv1beta1.OAuth2{ + TokenURL: "http://some-other", + ClientSecretFile: "/path/to/file", + }, + }, + }, + }, + }, + }, + }, + wantConfig: `global: + scrape_interval: 30s + external_labels: + prometheus: default/with-oauth2 +scrape_configs: +- job_name: podScrape/default/with-oauth2-simple/0 + kubernetes_sd_configs: + - role: pod + namespaces: + names: + - default + honor_labels: false + relabel_configs: + - action: drop + source_labels: + - __meta_kubernetes_pod_phase + regex: (Failed|Succeeded) + - action: keep + source_labels: + - __meta_kubernetes_pod_container_port_name + regex: "8085" + - source_labels: + - __meta_kubernetes_namespace + target_label: namespace + - source_labels: + - __meta_kubernetes_pod_container_name + target_label: container + - source_labels: + - __meta_kubernetes_pod_name + target_label: pod + - target_label: job + replacement: default/with-oauth2-simple + - target_label: endpoint + replacement: "8085" + oauth2: + token_url: http://some-url +- job_name: podScrape/default/with-oauth2-simple/1 + kubernetes_sd_configs: + - role: pod + namespaces: + names: + - default + honor_labels: false + relabel_configs: + - action: drop + source_labels: + - __meta_kubernetes_pod_phase + regex: (Failed|Succeeded) + - action: keep + source_labels: + - __meta_kubernetes_pod_container_port_name + regex: "8085" + - source_labels: + - __meta_kubernetes_namespace + target_label: namespace + - source_labels: + - __meta_kubernetes_pod_container_name + target_label: container + - source_labels: + - __meta_kubernetes_pod_name + target_label: pod + - target_label: job + replacement: default/with-oauth2-simple + - target_label: endpoint + replacement: "8085" + oauth2: + client_secret_file: /path/to/file + token_url: http://some-other +`, + }) +} + +func TestScrapeObjectFailedStatus(t *testing.T) { + + type getStatusMeta interface { + GetStatusMetadata() *vmv1beta1.StatusMetadata + } + f := func(so client.Object) { + t.Helper() + expectedConfig := `global: + scrape_interval: 30s + external_labels: + prometheus: default/vmsingle +scrape_configs: [] +` + ctx := context.TODO() + testClient := k8stools.GetTestClientWithClientObjects([]client.Object{so}) + build.AddDefaults(testClient.Scheme()) + + cr := &vmv1beta1.VMSingle{ + ObjectMeta: metav1.ObjectMeta{ + Name: "vmsingle", + Namespace: "default", + }, + Spec: vmv1beta1.VMSingleSpec{ + CommonScrapeParams: vmv1beta1.CommonScrapeParams{ + IngestOnlyMode: ptr.To(false), + SelectAllByDefault: true, + }, + }, + } + ac := getAssetsCache(ctx, testClient, cr) + if err := createOrUpdateScrapeConfig(ctx, testClient, cr, nil, nil, ac); err != nil { + t.Errorf("createOrUpdateScrapeConfig() error = %s", err) + } + var configSecret corev1.Secret + if err := testClient.Get(ctx, types.NamespacedName{Namespace: cr.Namespace, Name: cr.PrefixedName()}, &configSecret); err != nil { + t.Fatalf("cannot get vmsingle config secret: %s", err) + } + + gotCfg := configSecret.Data[scrapeGzippedFilename] + cfgB := bytes.NewBuffer(gotCfg) + gr, err := gzip.NewReader(cfgB) + if err != nil { + t.Fatalf("er: %s", err) + } + data, err := io.ReadAll(gr) + if err != nil { + t.Fatalf("cannot read cfg: %s", err) + } + gr.Close() + assert.Equal(t, expectedConfig, string(data)) + + if err := testClient.Get(ctx, types.NamespacedName{Name: so.GetName(), Namespace: so.GetNamespace()}, so); err != nil { + t.Fatalf("cannot reload object: %s", err) + } + status := so.(getStatusMeta).GetStatusMetadata() + assert.Equal(t, vmv1beta1.UpdateStatusFailed, status.UpdateStatus) + assert.NotEmpty(t, status.Reason) + assert.Len(t, status.Conditions, 1) + + } + commonMeta := metav1.ObjectMeta{ + Name: "invalid", + Namespace: "default", + } + + // invalid selector + f(&vmv1beta1.VMProbe{ + ObjectMeta: commonMeta, + Spec: vmv1beta1.VMProbeSpec{ + Targets: vmv1beta1.VMProbeTargets{ + Ingress: &vmv1beta1.ProbeTargetIngress{ + Selector: *metav1.SetAsLabelSelector(map[string]string{"alb.ingress.kubernetes.io/tags": "Environment=devl"}), + }, + }, + }, + }, + ) + // missing refs + f(&vmv1beta1.VMScrapeConfig{ + ObjectMeta: commonMeta, + Spec: vmv1beta1.VMScrapeConfigSpec{ + ConsulSDConfigs: []vmv1beta1.ConsulSDConfig{ + { + Server: "http://consul.example.com", + BasicAuth: &vmv1beta1.BasicAuth{ + Username: corev1.SecretKeySelector{ + Key: "username", + LocalObjectReference: corev1.LocalObjectReference{ + Name: "auth", + }, + }, + }, + }, + }, + }, + }, + ) + commonEndpointAuthWithMissingRef := vmv1beta1.EndpointAuth{ + BasicAuth: &vmv1beta1.BasicAuth{ + Username: corev1.SecretKeySelector{ + Key: "username", + LocalObjectReference: corev1.LocalObjectReference{ + Name: "auth", + }, + }, + }, + } + f(&vmv1beta1.VMProbe{ + ObjectMeta: commonMeta, + Spec: vmv1beta1.VMProbeSpec{ + EndpointAuth: commonEndpointAuthWithMissingRef, + }, + }) + + f(&vmv1beta1.VMStaticScrape{ + ObjectMeta: commonMeta, + Spec: vmv1beta1.VMStaticScrapeSpec{ + TargetEndpoints: []*vmv1beta1.TargetEndpoint{ + { + EndpointAuth: commonEndpointAuthWithMissingRef, + }, + }, + }, + }) + f(&vmv1beta1.VMNodeScrape{ + ObjectMeta: commonMeta, + Spec: vmv1beta1.VMNodeScrapeSpec{ + EndpointAuth: commonEndpointAuthWithMissingRef, + }, + }) + + f(&vmv1beta1.VMPodScrape{ + ObjectMeta: commonMeta, + Spec: vmv1beta1.VMPodScrapeSpec{ + PodMetricsEndpoints: []vmv1beta1.PodMetricsEndpoint{ + { + EndpointAuth: commonEndpointAuthWithMissingRef, + }, + }, + }, + }) + + f(&vmv1beta1.VMServiceScrape{ + ObjectMeta: commonMeta, + Spec: vmv1beta1.VMServiceScrapeSpec{ + Endpoints: []vmv1beta1.Endpoint{ + { + EndpointAuth: commonEndpointAuthWithMissingRef, + }, + }, + }, + }) + + // missing scrapeClass + f(&vmv1beta1.VMServiceScrape{ + ObjectMeta: commonMeta, + Spec: vmv1beta1.VMServiceScrapeSpec{ + ScrapeClassName: ptr.To("non-exist"), + Endpoints: []vmv1beta1.Endpoint{ + { + Port: "9090", + }, + }, + }, + }) + + // missing scrapeClass + f(&vmv1beta1.VMPodScrape{ + ObjectMeta: commonMeta, + Spec: vmv1beta1.VMPodScrapeSpec{ + ScrapeClassName: ptr.To("non-exist"), + PodMetricsEndpoints: []vmv1beta1.PodMetricsEndpoint{ + { + Port: ptr.To("9090"), + }, + }, + }, + }, + ) + +} diff --git a/internal/controller/operator/factory/vmsingle/vmsingle.go b/internal/controller/operator/factory/vmsingle/vmsingle.go index 104f0ed05..4b4b24c12 100644 --- a/internal/controller/operator/factory/vmsingle/vmsingle.go +++ b/internal/controller/operator/factory/vmsingle/vmsingle.go @@ -1,6 +1,7 @@ package vmsingle import ( + "bytes" "context" "fmt" "path" @@ -9,6 +10,7 @@ import ( "gopkg.in/yaml.v2" 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/labels" "k8s.io/apimachinery/pkg/util/intstr" @@ -21,11 +23,19 @@ import ( "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/finalize" "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/k8stools" "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/reconcile" + "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/vmscrapes" ) const ( - dataDataDir = "/victoria-metrics-data" - streamAggrSecretKey = "config.yaml" + confDir = "/etc/vm/config" + confOutDir = "/etc/vm/config_out" + tlsAssetsDir = "/etc/vm-tls/certs" + dataDir = "/victoria-metrics-data" + dataVolumeName = "data" + streamAggrSecretKey = "config.yaml" + relabelingName = "relabeling.yaml" + scrapeGzippedFilename = "scrape.yaml.gz" + configFilename = "scrape.yaml" ) func createStorage(ctx context.Context, rclient client.Client, cr, prevCR *vmv1beta1.VMSingle) error { @@ -69,10 +79,7 @@ func CreateOrUpdate(ctx context.Context, cr *vmv1beta1.VMSingle, rclient client. return fmt.Errorf("cannot delete objects from prev state: %w", err) } } - ac := getAssetsCache(ctx, rclient) - if err := createOrUpdateStreamAggrConfig(ctx, rclient, cr, prevCR, ac); err != nil { - return fmt.Errorf("cannot update stream aggregation config for vmsingle: %w", err) - } + ingestOnlyMode := ptr.Deref(cr.Spec.IngestOnlyMode, true) if cr.IsOwnsServiceAccount() { var prevSA *corev1.ServiceAccount if prevCR != nil { @@ -81,6 +88,11 @@ func CreateOrUpdate(ctx context.Context, cr *vmv1beta1.VMSingle, rclient client. if err := reconcile.ServiceAccount(ctx, rclient, build.ServiceAccount(cr), prevSA); err != nil { return fmt.Errorf("failed create service account: %w", err) } + if !ingestOnlyMode { + if err := createK8sAPIAccess(ctx, rclient, cr, prevCR, config.IsClusterWideAccessAllowed()); err != nil { + return fmt.Errorf("cannot create vmsingle role and binding for it, err: %w", err) + } + } } if cr.Spec.Storage != nil { @@ -100,6 +112,18 @@ func CreateOrUpdate(ctx context.Context, cr *vmv1beta1.VMSingle, rclient client. return fmt.Errorf("cannot create serviceScrape for vmsingle: %w", err) } } + + ac := getAssetsCache(ctx, rclient, cr) + if err := createOrUpdateScrapeConfig(ctx, rclient, cr, prevCR, nil, ac); err != nil { + return err + } + if err := createOrUpdateRelabelConfigsAssets(ctx, rclient, cr, prevCR, ac); err != nil { + return fmt.Errorf("cannot update relabeling asset for vmsingle: %w", err) + } + if err := createOrUpdateStreamAggrConfig(ctx, rclient, cr, prevCR, ac); err != nil { + return fmt.Errorf("cannot update stream aggregation config for vmsingle: %w", err) + } + var prevDeploy *appsv1.Deployment if prevCR != nil { prevDeploy, err = newDeploy(ctx, prevCR) @@ -117,7 +141,7 @@ func CreateOrUpdate(ctx context.Context, cr *vmv1beta1.VMSingle, rclient client. func newDeploy(ctx context.Context, cr *vmv1beta1.VMSingle) (*appsv1.Deployment, error) { - podSpec, err := makeSpec(ctx, cr) + podSpec, err := newPodSpec(ctx, cr) if err != nil { return nil, err } @@ -147,14 +171,14 @@ func newDeploy(ctx context.Context, cr *vmv1beta1.VMSingle) (*appsv1.Deployment, return depSpec, nil } -func makeSpec(ctx context.Context, cr *vmv1beta1.VMSingle) (*corev1.PodTemplateSpec, error) { +func newPodSpec(ctx context.Context, cr *vmv1beta1.VMSingle) (*corev1.PodTemplateSpec, error) { var args []string if cr.Spec.RetentionPeriod != "" { args = append(args, fmt.Sprintf("-retentionPeriod=%s", cr.Spec.RetentionPeriod)) } - storagePath := dataDataDir + storagePath := dataDir if cr.Spec.StorageDataPath != "" { storagePath = cr.Spec.StorageDataPath } @@ -183,6 +207,8 @@ func makeSpec(ctx context.Context, cr *vmv1beta1.VMSingle) (*corev1.PodTemplateS ports = append(ports, corev1.ContainerPort{Name: "http", Protocol: "TCP", ContainerPort: intstr.Parse(cr.Spec.Port).IntVal}) ports = build.AppendInsertPorts(ports, cr.Spec.InsertPorts) + var crMounts []corev1.VolumeMount + var pvcSrc *corev1.PersistentVolumeClaimVolumeSource if cr.Spec.Storage != nil { pvcSrc = &corev1.PersistentVolumeClaimVolumeSource{ @@ -194,6 +220,57 @@ func makeSpec(ctx context.Context, cr *vmv1beta1.VMSingle) (*corev1.PodTemplateS if err != nil { return nil, err } + + ingestOnlyMode := ptr.Deref(cr.Spec.IngestOnlyMode, true) + if !ingestOnlyMode { + args = append(args, fmt.Sprintf("-promscrape.config=%s", path.Join(confOutDir, configFilename))) + + // preserve order of volumes and volumeMounts + // it must prevent vmsingle restarts during operator version change + volumes = append(volumes, corev1.Volume{ + Name: string(build.TLSAssetsResourceKind), + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: build.ResourceName(build.TLSAssetsResourceKind, cr), + }, + }, + }) + + volumes = append(volumes, + corev1.Volume{ + Name: "config-out", + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }, + }, + ) + volumes = append(volumes, corev1.Volume{ + Name: string(build.SecretConfigResourceKind), + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: build.ResourceName(build.SecretConfigResourceKind, cr), + }, + }, + }) + m := corev1.VolumeMount{ + Name: "config-out", + MountPath: confOutDir, + } + crMounts = append(crMounts, m) + m.ReadOnly = true + vmMounts = append(vmMounts, m) + vmMounts = append(vmMounts, corev1.VolumeMount{ + Name: string(build.TLSAssetsResourceKind), + MountPath: tlsAssetsDir, + ReadOnly: true, + }) + vmMounts = append(vmMounts, corev1.VolumeMount{ + Name: string(build.SecretConfigResourceKind), + MountPath: confDir, + ReadOnly: true, + }) + } + commonMounts := vmMounts if cr.Spec.VMBackup != nil && cr.Spec.VMBackup.CredentialsSecret != nil { @@ -234,14 +311,24 @@ func makeSpec(ctx context.Context, cr *vmv1beta1.VMSingle) (*corev1.PodTemplateS }, }, }) - vmMounts = append(vmMounts, corev1.VolumeMount{ + cvm := corev1.VolumeMount{ Name: k8stools.SanitizeVolumeName("configmap-" + c), ReadOnly: true, MountPath: path.Join(vmv1beta1.ConfigMapsDir, c), - }) + } + vmMounts = append(vmMounts, cvm) + crMounts = append(crMounts, cvm) } + mountsLen := len(vmMounts) volumes, vmMounts = build.StreamAggrVolumeTo(volumes, vmMounts, cr) + volumes, vmMounts = build.RelabelVolumeTo(volumes, vmMounts, cr) + crMounts = append(crMounts, vmMounts[mountsLen:]...) + + relabelKeys := []string{"relabel.yaml"} + relabelConfigs := []*vmv1beta1.CommonRelabelParams{&cr.Spec.CommonRelabelParams} + args = build.RelabelArgsTo(args, "relabelConfig", relabelKeys, relabelConfigs...) + streamAggrKeys := []string{streamAggrSecretKey} streamAggrConfigs := []*vmv1beta1.StreamAggrConfig{cr.Spec.StreamAggrConfig} args = build.StreamAggrArgsTo(args, "streamAggr", streamAggrKeys, streamAggrConfigs...) @@ -270,9 +357,25 @@ func makeSpec(ctx context.Context, cr *vmv1beta1.VMSingle) (*corev1.PodTemplateS } vmsingleContainer = build.Probe(vmsingleContainer, cr) + useStrictSecurity := ptr.Deref(cr.Spec.UseStrictSecurity, cfg.EnableStrictSecurity) - operatorContainers := []corev1.Container{vmsingleContainer} - var initContainers []corev1.Container + containers := []corev1.Container{vmsingleContainer} + var ic []corev1.Container + + if !ingestOnlyMode || cr.HasAnyRelabellingConfigs() || cr.HasAnyStreamAggrRule() { + ss := &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: cr.PrefixedName(), + }, + Key: configFilename, + } + configReloader := build.ConfigReloaderContainer(false, cr, crMounts, ss) + containers = append(containers, configReloader) + if !ingestOnlyMode { + ic = append(ic, build.ConfigReloaderContainer(true, cr, crMounts, ss)) + build.AddStrictSecuritySettingsToContainers(cr.Spec.SecurityContext, ic, useStrictSecurity) + } + } 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) @@ -280,7 +383,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 && @@ -290,19 +393,20 @@ 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, cfg.EnableStrictSecurity)) - ic, err := k8stools.MergePatchContainers(initContainers, cr.Spec.InitContainers) + enableStrictSecurity := ptr.Deref(cr.Spec.UseStrictSecurity, cfg.EnableStrictSecurity) + build.AddStrictSecuritySettingsToContainers(cr.Spec.SecurityContext, ic, enableStrictSecurity) + 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, cfg.EnableStrictSecurity)) - containers, err := k8stools.MergePatchContainers(operatorContainers, cr.Spec.Containers) + build.AddStrictSecuritySettingsToContainers(cr.Spec.SecurityContext, containers, enableStrictSecurity) + containers, err = k8stools.MergePatchContainers(containers, cr.Spec.Containers) if err != nil { return nil, err } @@ -378,6 +482,51 @@ func createOrUpdateService(ctx context.Context, rclient client.Client, cr, prevC return newService, nil } +// buildRelabelingsAssets combines all possible relabeling config configuration and adding it to the configmap. +func buildRelabelingsAssets(cr *vmv1beta1.VMSingle, ac *build.AssetsCache) (*corev1.ConfigMap, error) { + cm := &corev1.ConfigMap{ + ObjectMeta: build.ResourceMeta(build.RelabelConfigResourceKind, cr), + Data: make(map[string]string), + } + if len(cr.Spec.InlineRelabelConfig) > 0 { + rcs := vmscrapes.AddRelabelConfigs(nil, cr.Spec.InlineRelabelConfig) + data, err := yaml.Marshal(rcs) + if err != nil { + return nil, fmt.Errorf("cannot serialize relabelConfig as yaml: %w", err) + } + if len(data) > 0 { + cm.Data[relabelingName] = string(data) + } + } + if cr.Spec.RelabelConfig != nil { + // need to fetch content from + data, err := ac.LoadKeyFromConfigMap(cr.Namespace, cr.Spec.RelabelConfig) + if err != nil { + return nil, fmt.Errorf("cannot fetch configmap: %s, err: %w", cr.Spec.RelabelConfig.Name, err) + } + if len(data) > 0 { + cm.Data[relabelingName] += data + } + } + return cm, nil +} + +// createOrUpdateRelabelConfigsAssets builds relabeling configs for vmsingle at separate configmap, serialized as yaml +func createOrUpdateRelabelConfigsAssets(ctx context.Context, rclient client.Client, cr, prevCR *vmv1beta1.VMSingle, ac *build.AssetsCache) error { + if !cr.HasAnyRelabellingConfigs() { + return nil + } + assestsCM, err := buildRelabelingsAssets(cr, ac) + if err != nil { + return err + } + var prevConfigMeta *metav1.ObjectMeta + if prevCR != nil { + prevConfigMeta = ptr.To(build.ResourceMeta(build.RelabelConfigResourceKind, prevCR)) + } + return reconcile.ConfigMap(ctx, rclient, assestsCM, prevConfigMeta) +} + // buildStreamAggrConfig build configmap with stream aggregation config for vmsingle. func buildStreamAggrConfig(cr *vmv1beta1.VMSingle, ac *build.AssetsCache) (*corev1.ConfigMap, error) { cfgCM := &corev1.ConfigMap{ @@ -427,6 +576,7 @@ func deleteOrphaned(ctx context.Context, rclient client.Client, cr *vmv1beta1.VM owner := cr.AsOwner() objMeta := metav1.ObjectMeta{Name: cr.PrefixedName(), Namespace: cr.Namespace} + cfg := config.MustGetBaseConfig() disableSelfScrape := cfg.DisableSelfServiceScrapeCreation if ptr.Deref(cr.Spec.DisableSelfServiceScrape, disableSelfScrape) { @@ -450,11 +600,139 @@ func deleteOrphaned(ctx context.Context, rclient client.Client, cr *vmv1beta1.VM if err := finalize.SafeDeleteWithFinalizer(ctx, rclient, &corev1.ServiceAccount{ObjectMeta: objMeta}, &owner); err != nil { return fmt.Errorf("cannot remove serviceaccount: %w", err) } + + rbacMeta := metav1.ObjectMeta{Name: cr.GetClusterRoleName(), Namespace: cr.Namespace} + var objects []client.Object + if config.IsClusterWideAccessAllowed() { + objects = []client.Object{ + &rbacv1.ClusterRoleBinding{ObjectMeta: rbacMeta}, + &rbacv1.ClusterRole{ObjectMeta: rbacMeta}, + } + } else { + objects = []client.Object{ + &rbacv1.RoleBinding{ObjectMeta: rbacMeta}, + &rbacv1.Role{ObjectMeta: rbacMeta}, + } + } + owner := cr.AsCRDOwner() + for _, o := range objects { + if err := finalize.SafeDeleteWithFinalizer(ctx, rclient, o, owner); err != nil { + return fmt.Errorf("cannot remove %T: %w", o, err) + } + } } return nil } -func getAssetsCache(ctx context.Context, rclient client.Client) *build.AssetsCache { - cfg := map[build.ResourceKind]*build.ResourceCfg{} +func getAssetsCache(ctx context.Context, rclient client.Client, cr *vmv1beta1.VMSingle) *build.AssetsCache { + cfg := map[build.ResourceKind]*build.ResourceCfg{ + build.SecretConfigResourceKind: { + MountDir: confDir, + SecretName: build.ResourceName(build.SecretConfigResourceKind, cr), + }, + build.TLSAssetsResourceKind: { + MountDir: tlsAssetsDir, + SecretName: build.ResourceName(build.TLSAssetsResourceKind, cr), + }, + } return build.NewAssetsCache(ctx, rclient, cfg) } + +// CreateOrUpdateScrapeConfig builds scrape configuration for VMSingle +func CreateOrUpdateScrapeConfig(ctx context.Context, rclient client.Client, cr *vmv1beta1.VMSingle, childObject client.Object) error { + var prevCR *vmv1beta1.VMSingle + if cr.ParsedLastAppliedSpec != nil { + prevCR = cr.DeepCopy() + prevCR.Spec = *cr.ParsedLastAppliedSpec + } + ac := getAssetsCache(ctx, rclient, cr) + if err := createOrUpdateScrapeConfig(ctx, rclient, cr, prevCR, childObject, ac); err != nil { + return err + } + return nil +} + +func createOrUpdateScrapeConfig(ctx context.Context, rclient client.Client, cr, prevCR *vmv1beta1.VMSingle, childObject client.Object, ac *build.AssetsCache) error { + ingestOnlyMode := ptr.Deref(cr.Spec.IngestOnlyMode, true) + if ingestOnlyMode { + return nil + } + + pos := &vmscrapes.ParsedObjects{ + Namespace: cr.Namespace, + APIServerConfig: cr.Spec.APIServerConfig, + HasClusterWideAccess: config.IsClusterWideAccessAllowed() || !cr.IsOwnsServiceAccount(), + ExternalLabels: buildExternalLabels(cr), + } + sp := &cr.Spec.CommonScrapeParams + if err := pos.Init(ctx, rclient, sp); err != nil { + return err + } + pos.ValidateObjects(sp) + + // Update secret based on the most recent configuration. + generatedConfig, err := pos.GenerateConfig( + ctx, + sp, + ac, + ) + if err != nil { + return fmt.Errorf("generating config for vmsingle failed: %w", err) + } + + for kind, secret := range ac.GetOutput() { + var prevSecretMeta *metav1.ObjectMeta + if prevCR != nil { + prevSecretMeta = ptr.To(build.ResourceMeta(kind, prevCR)) + } + if kind == build.SecretConfigResourceKind { + // Compress config to avoid 1mb secret limit for a while + var buf bytes.Buffer + if err = build.GzipConfig(&buf, generatedConfig); err != nil { + return fmt.Errorf("cannot gzip config for vmsingle: %w", err) + } + secret.Data[scrapeGzippedFilename] = buf.Bytes() + } + secret.ObjectMeta = build.ResourceMeta(kind, cr) + secret.Annotations = map[string]string{ + "generated": "true", + } + if err := reconcile.Secret(ctx, rclient, &secret, prevSecretMeta); err != nil { + return err + } + } + + parentName := fmt.Sprintf("%s.%s.vmsingle", cr.Name, cr.Namespace) + if err := pos.UpdateStatusesForScrapeObjects(ctx, rclient, parentName, childObject); err != nil { + return err + } + + return nil +} + +func buildExternalLabels(cr *vmv1beta1.VMSingle) map[string]string { + m := map[string]string{} + sp := cr.Spec.CommonScrapeParams + + // Use "prometheus" external label name by default if field is missing. + // in case of migration from prometheus to vmsingle, it helps to have same labels + // Do not add external label if field is set to empty string. + prometheusExternalLabelName := "prometheus" + labelName := sp.ExternalLabelName + if labelName != nil { + if *labelName != "" { + prometheusExternalLabelName = *labelName + } else { + prometheusExternalLabelName = "" + } + } + + if prometheusExternalLabelName != "" { + m[prometheusExternalLabelName] = fmt.Sprintf("%s/%s", cr.Namespace, cr.Name) + } + + for n, v := range sp.ExternalLabels { + m[n] = v + } + return m +} diff --git a/internal/controller/operator/vmnodescrape_controller.go b/internal/controller/operator/vmnodescrape_controller.go index 4c883cae5..fda665996 100644 --- a/internal/controller/operator/vmnodescrape_controller.go +++ b/internal/controller/operator/vmnodescrape_controller.go @@ -31,6 +31,7 @@ import ( "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/k8stools" "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/logger" "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/vmagent" + "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/vmsingle" ) // VMNodeScrapeReconciler reconciles a VMNodeScrape object @@ -75,53 +76,91 @@ func (r *VMNodeScrapeReconciler) Reconcile(ctx context.Context, req ctrl.Request if instance.Spec.ParsingError != "" { return result, &parsingError{instance.Spec.ParsingError, "vmnodescrape"} } - if agentReconcileLimit.MustThrottleReconcile() { - // fast path, rate limited - return - } + if !agentReconcileLimit.MustThrottleReconcile() { + agentSync.Lock() + + var objects vmv1beta1.VMAgentList + if err := k8stools.ListObjectsByNamespace(ctx, r.Client, r.BaseConf.WatchNamespaces, func(dst *vmv1beta1.VMAgentList) { + objects.Items = append(objects.Items, dst.Items...) + }); err != nil { + return result, fmt.Errorf("cannot list VMAgents for VMNodeScrape: %w", err) + } - agentSync.Lock() - defer agentSync.Unlock() + for i := range objects.Items { + item := &objects.Items[i] + if !item.DeletionTimestamp.IsZero() || item.Spec.ParsingError != "" || item.IsNodeScrapeUnmanaged() { + continue + } + l := l.WithValues("vmagent", item.Name, "parent_namespace", item.Namespace) + ctx := logger.AddToContext(ctx, l) + + // only check selector when deleting object, + // since labels can be changed when updating and we can't tell if it was selected before, and we can't tell if it's creating or updating. + if !instance.DeletionTimestamp.IsZero() { + opts := &k8stools.SelectorOpts{ + SelectAll: item.Spec.SelectAllByDefault, + NamespaceSelector: item.Spec.NodeScrapeNamespaceSelector, + ObjectSelector: item.Spec.NodeScrapeSelector, + DefaultNamespace: instance.Namespace, + } + match, err := isSelectorsMatchesTargetCRD(ctx, r.Client, instance, item, opts) + if err != nil { + l.Error(err, "cannot match VMAgent and VMNodeScrape") + continue + } + if !match { + continue + } + } - var objects vmv1beta1.VMAgentList - if err := k8stools.ListObjectsByNamespace(ctx, r.Client, r.BaseConf.WatchNamespaces, func(dst *vmv1beta1.VMAgentList) { - objects.Items = append(objects.Items, dst.Items...) - }); err != nil { - return result, fmt.Errorf("cannot list vmagents for vmnodescrape: %w", err) + if err := vmagent.CreateOrUpdateScrapeConfig(ctx, r, item, instance); err != nil { + continue + } + } + agentSync.Unlock() } - for i := range objects.Items { - item := &objects.Items[i] - if !item.DeletionTimestamp.IsZero() || item.Spec.ParsingError != "" || item.IsNodeScrapeUnmanaged() { - continue - } - l := l.WithValues("vmagent", item.Name, "parent_namespace", item.Namespace) - ctx := logger.AddToContext(ctx, l) - if item.Spec.DaemonSetMode { - continue + if !vmsingleReconcileLimit.MustThrottleReconcile() { + vmsingleSync.Lock() + + var objects vmv1beta1.VMSingleList + if err := k8stools.ListObjectsByNamespace(ctx, r.Client, r.BaseConf.WatchNamespaces, func(dst *vmv1beta1.VMSingleList) { + objects.Items = append(objects.Items, dst.Items...) + }); err != nil { + return result, fmt.Errorf("cannot list VMSingles for VMNodeScrape: %w", err) } - // only check selector when deleting object, - // since labels can be changed when updating and we can't tell if it was selected before, and we can't tell if it's creating or updating. - if !instance.DeletionTimestamp.IsZero() { - opts := &k8stools.SelectorOpts{ - SelectAll: item.Spec.SelectAllByDefault, - NamespaceSelector: item.Spec.NodeScrapeNamespaceSelector, - ObjectSelector: item.Spec.NodeScrapeSelector, - DefaultNamespace: instance.Namespace, - } - match, err := isSelectorsMatchesTargetCRD(ctx, r.Client, instance, item, opts) - if err != nil { - l.Error(err, "cannot match vmagent and vmnodescrape") + + for i := range objects.Items { + item := &objects.Items[i] + if !item.DeletionTimestamp.IsZero() || item.Spec.ParsingError != "" || item.IsNodeScrapeUnmanaged() { continue } - if !match { - continue + l := l.WithValues("vmsingle", item.Name, "parent_namespace", item.Namespace) + ctx := logger.AddToContext(ctx, l) + // only check selector when deleting object, + // since labels can be changed when updating and we can't tell if it was selected before, and we can't tell if it's creating or updating. + if !instance.DeletionTimestamp.IsZero() { + opts := &k8stools.SelectorOpts{ + SelectAll: item.Spec.SelectAllByDefault, + NamespaceSelector: item.Spec.NodeScrapeNamespaceSelector, + ObjectSelector: item.Spec.NodeScrapeSelector, + DefaultNamespace: instance.Namespace, + } + match, err := isSelectorsMatchesTargetCRD(ctx, r.Client, instance, item, opts) + if err != nil { + l.Error(err, "cannot match VMSingle and VMNodeScrape") + continue + } + if !match { + continue + } } - } - if err := vmagent.CreateOrUpdateScrapeConfig(ctx, r, item, instance); err != nil { - continue + if err := vmsingle.CreateOrUpdateScrapeConfig(ctx, r, item, instance); err != nil { + continue + } } + vmsingleSync.Unlock() } return diff --git a/internal/controller/operator/vmpodscrape_controller.go b/internal/controller/operator/vmpodscrape_controller.go index d0b428c4b..9551edd18 100644 --- a/internal/controller/operator/vmpodscrape_controller.go +++ b/internal/controller/operator/vmpodscrape_controller.go @@ -31,6 +31,7 @@ import ( "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/k8stools" "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/logger" "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/vmagent" + "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/vmsingle" ) // VMPodScrapeReconciler reconciles a VMPodScrape object @@ -74,50 +75,92 @@ func (r *VMPodScrapeReconciler) Reconcile(ctx context.Context, req ctrl.Request) if instance.Spec.ParsingError != "" { return result, &parsingError{instance.Spec.ParsingError, "vmpodscrape"} } - if agentReconcileLimit.MustThrottleReconcile() { - return - } + if !agentReconcileLimit.MustThrottleReconcile() { + agentSync.Lock() + + var objects vmv1beta1.VMAgentList + if err := k8stools.ListObjectsByNamespace(ctx, r.Client, r.BaseConf.WatchNamespaces, func(dst *vmv1beta1.VMAgentList) { + objects.Items = append(objects.Items, dst.Items...) + }); err != nil { + return result, fmt.Errorf("cannot list VMAgents for VMPodScrape: %w", err) + } - agentSync.Lock() - defer agentSync.Unlock() + for i := range objects.Items { + item := &objects.Items[i] + if !item.DeletionTimestamp.IsZero() || item.Spec.ParsingError != "" || item.IsPodScrapeUnmanaged() { + continue + } + l := l.WithValues("vmagent", item.Name, "parent_namespace", item.Namespace) + ctx := logger.AddToContext(ctx, l) + + // only check selector when deleting object, + // since labels can be changed when updating and we can't tell if it was selected before, and we can't tell if it's creating or updating. + if !instance.DeletionTimestamp.IsZero() { + opts := &k8stools.SelectorOpts{ + SelectAll: item.Spec.SelectAllByDefault, + NamespaceSelector: item.Spec.PodScrapeNamespaceSelector, + ObjectSelector: item.Spec.PodScrapeSelector, + DefaultNamespace: instance.Namespace, + } + match, err := isSelectorsMatchesTargetCRD(ctx, r.Client, instance, item, opts) + if err != nil { + l.Error(err, "cannot match VMAgent and VMPodScrape") + continue + } + if !match { + continue + } + } - var objects vmv1beta1.VMAgentList - if err := k8stools.ListObjectsByNamespace(ctx, r.Client, r.BaseConf.WatchNamespaces, func(dst *vmv1beta1.VMAgentList) { - objects.Items = append(objects.Items, dst.Items...) - }); err != nil { - return result, fmt.Errorf("cannot list vmagents for vmpodscrape: %w", err) + if err := vmagent.CreateOrUpdateScrapeConfig(ctx, r, item, instance); err != nil { + continue + } + } + agentSync.Unlock() } - for i := range objects.Items { - item := &objects.Items[i] - if !item.DeletionTimestamp.IsZero() || item.Spec.ParsingError != "" || item.IsPodScrapeUnmanaged() { - continue + if !vmsingleReconcileLimit.MustThrottleReconcile() { + vmsingleSync.Lock() + + var objects vmv1beta1.VMSingleList + if err := k8stools.ListObjectsByNamespace(ctx, r.Client, r.BaseConf.WatchNamespaces, func(dst *vmv1beta1.VMSingleList) { + objects.Items = append(objects.Items, dst.Items...) + }); err != nil { + return result, fmt.Errorf("cannot list VMSingles for VMPodScrape: %w", err) } - l := l.WithValues("vmagent", item.Name, "parent_namespace", item.Namespace) - ctx := logger.AddToContext(ctx, l) - - // only check selector when deleting object, - // since labels can be changed when updating and we can't tell if it was selected before, and we can't tell if it's creating or updating. - if !instance.DeletionTimestamp.IsZero() { - opts := &k8stools.SelectorOpts{ - SelectAll: item.Spec.SelectAllByDefault, - NamespaceSelector: item.Spec.PodScrapeNamespaceSelector, - ObjectSelector: item.Spec.PodScrapeSelector, - DefaultNamespace: instance.Namespace, - } - match, err := isSelectorsMatchesTargetCRD(ctx, r.Client, instance, item, opts) - if err != nil { - l.Error(err, "cannot match vmagent and vmpodscrape") + + for i := range objects.Items { + item := &objects.Items[i] + if !item.DeletionTimestamp.IsZero() || item.Spec.ParsingError != "" || item.IsPodScrapeUnmanaged() { continue } - if !match { - continue + l := l.WithValues("vmsingle", item.Name, "parent_namespace", item.Namespace) + ctx := logger.AddToContext(ctx, l) + + // only check selector when deleting object, + // since labels can be changed when updating and we can't tell if it was selected before, and we can't tell if it's creating or updating. + if !instance.DeletionTimestamp.IsZero() { + opts := &k8stools.SelectorOpts{ + SelectAll: item.Spec.SelectAllByDefault, + NamespaceSelector: item.Spec.PodScrapeNamespaceSelector, + ObjectSelector: item.Spec.PodScrapeSelector, + DefaultNamespace: instance.Namespace, + } + match, err := isSelectorsMatchesTargetCRD(ctx, r.Client, instance, item, opts) + if err != nil { + l.Error(err, "cannot match VMSingle and VMPodScrape") + continue + } + if !match { + continue + } } - } - if err := vmagent.CreateOrUpdateScrapeConfig(ctx, r, item, instance); err != nil { - continue + if err := vmsingle.CreateOrUpdateScrapeConfig(ctx, r, item, instance); err != nil { + continue + } } + vmsingleSync.Unlock() } return diff --git a/internal/controller/operator/vmprobe_controller.go b/internal/controller/operator/vmprobe_controller.go index c38ef89d9..1b8b2e052 100644 --- a/internal/controller/operator/vmprobe_controller.go +++ b/internal/controller/operator/vmprobe_controller.go @@ -31,6 +31,7 @@ import ( "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/k8stools" "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/logger" "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/vmagent" + "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/vmsingle" ) // VMProbeReconciler reconciles a VMProbe object @@ -74,54 +75,95 @@ func (r *VMProbeReconciler) Reconcile(ctx context.Context, req ctrl.Request) (re if instance.Spec.ParsingError != "" { return result, &parsingError{instance.Spec.ParsingError, "vmprobescrape"} } - if agentReconcileLimit.MustThrottleReconcile() { - // fast path, rate limited - return - } + if !agentReconcileLimit.MustThrottleReconcile() { + agentSync.Lock() + + var objects vmv1beta1.VMAgentList + if err := k8stools.ListObjectsByNamespace(ctx, r.Client, r.BaseConf.WatchNamespaces, func(dst *vmv1beta1.VMAgentList) { + objects.Items = append(objects.Items, dst.Items...) + }); err != nil { + return result, fmt.Errorf("cannot list VMAgents for VMProbe: %w", err) + } - agentSync.Lock() - defer agentSync.Unlock() + for i := range objects.Items { + item := &objects.Items[i] + if !item.DeletionTimestamp.IsZero() || item.Spec.ParsingError != "" || item.IsProbeUnmanaged() { + continue + } + if item.Spec.DaemonSetMode { + continue + } + l := l.WithValues("vmagent", item.Name, "parent_namespace", item.Namespace) + ctx := logger.AddToContext(ctx, l) + + // only check selector when deleting object, + // since labels can be changed when updating and we can't tell if it was selected before, and we can't tell if it's creating or updating. + if !instance.DeletionTimestamp.IsZero() { + opts := &k8stools.SelectorOpts{ + SelectAll: item.Spec.SelectAllByDefault, + NamespaceSelector: item.Spec.ProbeNamespaceSelector, + ObjectSelector: item.Spec.ProbeSelector, + DefaultNamespace: instance.Namespace, + } + match, err := isSelectorsMatchesTargetCRD(ctx, r.Client, instance, item, opts) + if err != nil { + l.Error(err, "cannot match VMAgent and VMProbe") + continue + } + if !match { + continue + } + } - var objects vmv1beta1.VMAgentList - if err := k8stools.ListObjectsByNamespace(ctx, r.Client, r.BaseConf.WatchNamespaces, func(dst *vmv1beta1.VMAgentList) { - objects.Items = append(objects.Items, dst.Items...) - }); err != nil { - return result, fmt.Errorf("cannot list vmagents for vmprobe: %w", err) + if err := vmagent.CreateOrUpdateScrapeConfig(ctx, r, item, instance); err != nil { + continue + } + } + agentSync.Unlock() } - for i := range objects.Items { - item := &objects.Items[i] - if !item.DeletionTimestamp.IsZero() || item.Spec.ParsingError != "" || item.IsProbeUnmanaged() { - continue - } - if item.Spec.DaemonSetMode { - continue + if !vmsingleReconcileLimit.MustThrottleReconcile() { + vmsingleSync.Lock() + + var objects vmv1beta1.VMSingleList + if err := k8stools.ListObjectsByNamespace(ctx, r.Client, r.BaseConf.WatchNamespaces, func(dst *vmv1beta1.VMSingleList) { + objects.Items = append(objects.Items, dst.Items...) + }); err != nil { + return result, fmt.Errorf("cannot list VMSingles for VMProbe: %w", err) } - l := l.WithValues("vmagent", item.Name, "parent_namespace", item.Namespace) - ctx := logger.AddToContext(ctx, l) - - // only check selector when deleting object, - // since labels can be changed when updating and we can't tell if it was selected before, and we can't tell if it's creating or updating. - if !instance.DeletionTimestamp.IsZero() { - opts := &k8stools.SelectorOpts{ - SelectAll: item.Spec.SelectAllByDefault, - NamespaceSelector: item.Spec.ProbeNamespaceSelector, - ObjectSelector: item.Spec.ProbeSelector, - DefaultNamespace: instance.Namespace, - } - match, err := isSelectorsMatchesTargetCRD(ctx, r.Client, instance, item, opts) - if err != nil { - l.Error(err, "cannot match vmagent and vmprobe") + + for i := range objects.Items { + item := &objects.Items[i] + if !item.DeletionTimestamp.IsZero() || item.Spec.ParsingError != "" || item.IsProbeUnmanaged() { continue } - if !match { - continue + l := l.WithValues("vmsingle", item.Name, "parent_namespace", item.Namespace) + ctx := logger.AddToContext(ctx, l) + + // only check selector when deleting object, + // since labels can be changed when updating and we can't tell if it was selected before, and we can't tell if it's creating or updating. + if !instance.DeletionTimestamp.IsZero() { + opts := &k8stools.SelectorOpts{ + SelectAll: item.Spec.SelectAllByDefault, + NamespaceSelector: item.Spec.ProbeNamespaceSelector, + ObjectSelector: item.Spec.ProbeSelector, + DefaultNamespace: instance.Namespace, + } + match, err := isSelectorsMatchesTargetCRD(ctx, r.Client, instance, item, opts) + if err != nil { + l.Error(err, "cannot match VMSingle and VMProbe") + continue + } + if !match { + continue + } } - } - if err := vmagent.CreateOrUpdateScrapeConfig(ctx, r, item, instance); err != nil { - continue + if err := vmsingle.CreateOrUpdateScrapeConfig(ctx, r, item, instance); err != nil { + continue + } } + vmsingleSync.Unlock() } return } diff --git a/internal/controller/operator/vmscrapeconfig_controller.go b/internal/controller/operator/vmscrapeconfig_controller.go index c3505b858..62f578cd9 100644 --- a/internal/controller/operator/vmscrapeconfig_controller.go +++ b/internal/controller/operator/vmscrapeconfig_controller.go @@ -31,6 +31,7 @@ import ( "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/k8stools" "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/logger" "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/vmagent" + "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/vmsingle" ) // VMScrapeConfigReconciler reconciles a VMScrapeConfig object @@ -74,52 +75,91 @@ func (r *VMScrapeConfigReconciler) Reconcile(ctx context.Context, req ctrl.Reque if instance.Spec.ParsingError != "" { return result, &parsingError{instance.Spec.ParsingError, "vmscrapeconfig"} } - if agentReconcileLimit.MustThrottleReconcile() { - // fast path, rate limited - return - } - - agentSync.Lock() - defer agentSync.Unlock() - var objects vmv1beta1.VMAgentList - if err := k8stools.ListObjectsByNamespace(ctx, r.Client, r.BaseConf.WatchNamespaces, func(dst *vmv1beta1.VMAgentList) { - objects.Items = append(objects.Items, dst.Items...) - }); err != nil { - return result, fmt.Errorf("cannot list vmagents for vmscrapeconfig: %w", err) - } - - for i := range objects.Items { - item := &objects.Items[i] - if !item.DeletionTimestamp.IsZero() || item.Spec.ParsingError != "" || item.IsScrapeConfigUnmanaged() { - continue - } - l := l.WithValues("vmagent", item.Name, "parent_namespace", item.Namespace) - ctx := logger.AddToContext(ctx, l) - if item.Spec.DaemonSetMode { - continue + if !agentReconcileLimit.MustThrottleReconcile() { + agentSync.Lock() + var objects vmv1beta1.VMAgentList + if err := k8stools.ListObjectsByNamespace(ctx, r.Client, r.BaseConf.WatchNamespaces, func(dst *vmv1beta1.VMAgentList) { + objects.Items = append(objects.Items, dst.Items...) + }); err != nil { + return result, fmt.Errorf("cannot list VMAgents for VMScrapeConfig: %w", err) } - // only check selector when deleting object, - // since labels can be changed when updating and we can't tell if it was selected before, and we can't tell if it's creating or updating. - if !instance.DeletionTimestamp.IsZero() { - opts := &k8stools.SelectorOpts{ - SelectAll: item.Spec.SelectAllByDefault, - NamespaceSelector: item.Spec.ScrapeConfigNamespaceSelector, - ObjectSelector: item.Spec.ScrapeConfigSelector, - DefaultNamespace: instance.Namespace, + + for i := range objects.Items { + item := &objects.Items[i] + if !item.DeletionTimestamp.IsZero() || item.Spec.ParsingError != "" || item.IsScrapeConfigUnmanaged() { + continue } - match, err := isSelectorsMatchesTargetCRD(ctx, r.Client, instance, item, opts) - if err != nil { - l.Error(err, "cannot match vmagent and vmscrapeconfig") + l := l.WithValues("vmagent", item.Name, "parent_namespace", item.Namespace) + ctx := logger.AddToContext(ctx, l) + if item.Spec.DaemonSetMode { continue } - if !match { + // only check selector when deleting object, + // since labels can be changed when updating and we can't tell if it was selected before, and we can't tell if it's creating or updating. + if !instance.DeletionTimestamp.IsZero() { + opts := &k8stools.SelectorOpts{ + SelectAll: item.Spec.SelectAllByDefault, + NamespaceSelector: item.Spec.ScrapeConfigNamespaceSelector, + ObjectSelector: item.Spec.ScrapeConfigSelector, + DefaultNamespace: instance.Namespace, + } + match, err := isSelectorsMatchesTargetCRD(ctx, r.Client, instance, item, opts) + if err != nil { + l.Error(err, "cannot match VMAgent and VMScrapeConfig") + continue + } + if !match { + continue + } + } + + if err := vmagent.CreateOrUpdateScrapeConfig(ctx, r, item, instance); err != nil { continue } } + agentSync.Unlock() + } + + if !vmsingleReconcileLimit.MustThrottleReconcile() { + vmsingleSync.Lock() + var objects vmv1beta1.VMSingleList + if err := k8stools.ListObjectsByNamespace(ctx, r.Client, r.BaseConf.WatchNamespaces, func(dst *vmv1beta1.VMSingleList) { + objects.Items = append(objects.Items, dst.Items...) + }); err != nil { + return result, fmt.Errorf("cannot list VMSingles for VMScrapeConfig: %w", err) + } - if err := vmagent.CreateOrUpdateScrapeConfig(ctx, r, item, instance); err != nil { - continue + for i := range objects.Items { + item := &objects.Items[i] + if !item.DeletionTimestamp.IsZero() || item.Spec.ParsingError != "" || item.IsScrapeConfigUnmanaged() { + continue + } + l := l.WithValues("vmsingle", item.Name, "parent_namespace", item.Namespace) + ctx := logger.AddToContext(ctx, l) + // only check selector when deleting object, + // since labels can be changed when updating and we can't tell if it was selected before, and we can't tell if it's creating or updating. + if !instance.DeletionTimestamp.IsZero() { + opts := &k8stools.SelectorOpts{ + SelectAll: item.Spec.SelectAllByDefault, + NamespaceSelector: item.Spec.ScrapeConfigNamespaceSelector, + ObjectSelector: item.Spec.ScrapeConfigSelector, + DefaultNamespace: instance.Namespace, + } + match, err := isSelectorsMatchesTargetCRD(ctx, r.Client, instance, item, opts) + if err != nil { + l.Error(err, "cannot match VMSingle and VMScrapeConfig") + continue + } + if !match { + continue + } + } + + if err := vmsingle.CreateOrUpdateScrapeConfig(ctx, r, item, instance); err != nil { + continue + } } + vmsingleSync.Unlock() } return } diff --git a/internal/controller/operator/vmservicescrape_controller.go b/internal/controller/operator/vmservicescrape_controller.go index a6629998b..34ee48a95 100644 --- a/internal/controller/operator/vmservicescrape_controller.go +++ b/internal/controller/operator/vmservicescrape_controller.go @@ -31,6 +31,7 @@ import ( "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/k8stools" "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/logger" "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/vmagent" + "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/vmsingle" ) // VMServiceScrapeReconciler reconciles a VMServiceScrape object @@ -75,52 +76,91 @@ func (r *VMServiceScrapeReconciler) Reconcile(ctx context.Context, req ctrl.Requ return result, &parsingError{instance.Spec.ParsingError, "vmservicescrape"} } - if agentReconcileLimit.MustThrottleReconcile() { - // fast path, rate limited - return - } - - agentSync.Lock() - defer agentSync.Unlock() - var objects vmv1beta1.VMAgentList - if err := k8stools.ListObjectsByNamespace(ctx, r.Client, r.BaseConf.WatchNamespaces, func(dst *vmv1beta1.VMAgentList) { - objects.Items = append(objects.Items, dst.Items...) - }); err != nil { - return result, fmt.Errorf("cannot list vmagents for vmservicescrape: %w", err) - } - - for i := range objects.Items { - item := &objects.Items[i] - if !item.DeletionTimestamp.IsZero() || item.Spec.ParsingError != "" || item.IsServiceScrapeUnmanaged() { - continue - } - l := l.WithValues("vmagent", item.Name, "parent_namespace", item.Namespace) - ctx := logger.AddToContext(ctx, l) - if item.Spec.DaemonSetMode { - continue + if !agentReconcileLimit.MustThrottleReconcile() { + agentSync.Lock() + var objects vmv1beta1.VMAgentList + if err := k8stools.ListObjectsByNamespace(ctx, r.Client, r.BaseConf.WatchNamespaces, func(dst *vmv1beta1.VMAgentList) { + objects.Items = append(objects.Items, dst.Items...) + }); err != nil { + return result, fmt.Errorf("cannot list VMAgents for VMServiceScrape: %w", err) } - // only check selector when deleting object, - // since labels can be changed when updating and we can't tell if it was selected before, and we can't tell if it's creating or updating. - if !instance.DeletionTimestamp.IsZero() { - opts := &k8stools.SelectorOpts{ - SelectAll: item.Spec.SelectAllByDefault, - NamespaceSelector: item.Spec.ServiceScrapeNamespaceSelector, - ObjectSelector: item.Spec.ServiceScrapeSelector, - DefaultNamespace: instance.Namespace, + + for i := range objects.Items { + item := &objects.Items[i] + if !item.DeletionTimestamp.IsZero() || item.Spec.ParsingError != "" || item.IsServiceScrapeUnmanaged() { + continue } - match, err := isSelectorsMatchesTargetCRD(ctx, r.Client, instance, item, opts) - if err != nil { - l.Error(err, "cannot match vmagent and vmservicescrape") + l := l.WithValues("vmagent", item.Name, "parent_namespace", item.Namespace) + ctx := logger.AddToContext(ctx, l) + if item.Spec.DaemonSetMode { continue } - if !match { + // only check selector when deleting object, + // since labels can be changed when updating and we can't tell if it was selected before, and we can't tell if it's creating or updating. + if !instance.DeletionTimestamp.IsZero() { + opts := &k8stools.SelectorOpts{ + SelectAll: item.Spec.SelectAllByDefault, + NamespaceSelector: item.Spec.ServiceScrapeNamespaceSelector, + ObjectSelector: item.Spec.ServiceScrapeSelector, + DefaultNamespace: instance.Namespace, + } + match, err := isSelectorsMatchesTargetCRD(ctx, r.Client, instance, item, opts) + if err != nil { + l.Error(err, "cannot match VMAgent and VMServiceScrape") + continue + } + if !match { + continue + } + } + + if err := vmagent.CreateOrUpdateScrapeConfig(ctx, r, item, instance); err != nil { continue } } + agentSync.Unlock() + } + + if !vmsingleReconcileLimit.MustThrottleReconcile() { + vmsingleSync.Lock() + var objects vmv1beta1.VMSingleList + if err := k8stools.ListObjectsByNamespace(ctx, r.Client, r.BaseConf.WatchNamespaces, func(dst *vmv1beta1.VMSingleList) { + objects.Items = append(objects.Items, dst.Items...) + }); err != nil { + return result, fmt.Errorf("cannot list VMSingles for VMServiceScrape: %w", err) + } - if err := vmagent.CreateOrUpdateScrapeConfig(ctx, r, item, instance); err != nil { - continue + for i := range objects.Items { + item := &objects.Items[i] + if !item.DeletionTimestamp.IsZero() || item.Spec.ParsingError != "" || item.IsServiceScrapeUnmanaged() { + continue + } + l := l.WithValues("vmsingle", item.Name, "parent_namespace", item.Namespace) + ctx := logger.AddToContext(ctx, l) + // only check selector when deleting object, + // since labels can be changed when updating and we can't tell if it was selected before, and we can't tell if it's creating or updating. + if !instance.DeletionTimestamp.IsZero() { + opts := &k8stools.SelectorOpts{ + SelectAll: item.Spec.SelectAllByDefault, + NamespaceSelector: item.Spec.ServiceScrapeNamespaceSelector, + ObjectSelector: item.Spec.ServiceScrapeSelector, + DefaultNamespace: instance.Namespace, + } + match, err := isSelectorsMatchesTargetCRD(ctx, r.Client, instance, item, opts) + if err != nil { + l.Error(err, "cannot match VMSingle and VMServiceScrape") + continue + } + if !match { + continue + } + } + + if err := vmsingle.CreateOrUpdateScrapeConfig(ctx, r, item, instance); err != nil { + continue + } } + vmsingleSync.Unlock() } return } diff --git a/internal/controller/operator/vmsingle_controller.go b/internal/controller/operator/vmsingle_controller.go index ff22280a2..78b6ca924 100644 --- a/internal/controller/operator/vmsingle_controller.go +++ b/internal/controller/operator/vmsingle_controller.go @@ -19,6 +19,7 @@ package operator import ( "context" "fmt" + "sync" "github.com/go-logr/logr" appsv1 "k8s.io/api/apps/v1" @@ -30,10 +31,16 @@ 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" "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/vmsingle" ) +var ( + vmsingleSync sync.Mutex + vmsingleReconcileLimit = limiter.NewRateLimiter("vmsingle", 5) +) + // VMSingleReconciler reconciles a VMSingle object type VMSingleReconciler struct { client.Client @@ -57,11 +64,25 @@ func (r *VMSingleReconciler) Scheme() *runtime.Scheme { // Reconcile general reconcile method for controller // +kubebuilder:rbac:groups=operator.victoriametrics.com,resources=vmsingles,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=operator.victoriametrics.com,resources=vmsingles/status,verbs=get;update;patch // +kubebuilder:rbac:groups=operator.victoriametrics.com,resources=vmsingles/finalizers,verbs=* +// +kubebuilder:rbac:groups="",resources=pods,verbs=* +// +kubebuilder:rbac:groups="",resources=nodes,verbs=get;watch;list +// +kubebuilder:rbac:groups="",resources=nodes/proxy,verbs=get;watch;list +// +kubebuilder:rbac:groups="networking.k8s.io",resources=ingresses,verbs=get;watch;list +// +kubebuilder:rbac:groups="",resources=events,verbs=* +// +kubebuilder:rbac:groups="",resources=endpoints,verbs=* +// +kubebuilder:rbac:groups="",resources=endpointslices,verbs=get;watch;list +// +kubebuilder:rbac:groups="",resources=services,verbs=* +// +kubebuilder:rbac:groups="",resources=services/finalizers,verbs=* +// +kubebuilder:rbac:groups=monitoring.coreos.com,resources=*,verbs=* +// +kubebuilder:rbac:groups="",resources=namespaces,verbs=get;watch;list +// +kubebuilder:rbac:groups="rbac.authorization.k8s.io",resources=clusterrolebindings,verbs=get;create,update;list +// +kubebuilder:rbac:groups="rbac.authorization.k8s.io",resources=clusterroles,verbs=get;create,update;list +// +kubebuilder:rbac:groups="",resources=serviceaccounts,verbs=get;create,update;list // +kubebuilder:rbac:groups=apps,resources=deployments,verbs=* // +kubebuilder:rbac:groups=apps,resources=replicasets,verbs=* // +kubebuilder:rbac:groups="",resources=persistentvolumeclaims,verbs=* -// +kubebuilder:rbac:groups=operator.victoriametrics.com,resources=vmsingles/status,verbs=get;update;patch 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) diff --git a/internal/controller/operator/vmstaticscrape_controller.go b/internal/controller/operator/vmstaticscrape_controller.go index 5a0ff4444..7048a2597 100644 --- a/internal/controller/operator/vmstaticscrape_controller.go +++ b/internal/controller/operator/vmstaticscrape_controller.go @@ -15,6 +15,7 @@ import ( "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/k8stools" "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/logger" "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/vmagent" + "github.com/VictoriaMetrics/operator/internal/controller/operator/factory/vmsingle" ) // VMStaticScrapeReconciler reconciles a VMStaticScrape object @@ -54,53 +55,94 @@ func (r *VMStaticScrapeReconciler) Reconcile(ctx context.Context, req ctrl.Reque if instance.Spec.ParsingError != "" { return result, &parsingError{instance.Spec.ParsingError, "vmstaticscrape"} } - if agentReconcileLimit.MustThrottleReconcile() { - // fast path, rate limited - return ctrl.Result{}, nil - } - agentSync.Lock() - defer agentSync.Unlock() - - var objects vmv1beta1.VMAgentList - if err := k8stools.ListObjectsByNamespace(ctx, r.Client, r.BaseConf.WatchNamespaces, func(dst *vmv1beta1.VMAgentList) { - objects.Items = append(objects.Items, dst.Items...) - }); err != nil { - return result, fmt.Errorf("cannot list vmagents for vmstaticscrape: %w", err) - } - - for i := range objects.Items { - item := &objects.Items[i] - if !item.DeletionTimestamp.IsZero() || item.Spec.ParsingError != "" || item.IsStaticScrapeUnmanaged() { - continue - } - l := l.WithValues("vmagent", item.Name, "parent_namespace", item.Namespace) - ctx := logger.AddToContext(ctx, l) - if item.Spec.DaemonSetMode { - continue + if !agentReconcileLimit.MustThrottleReconcile() { + agentSync.Lock() + var objects vmv1beta1.VMAgentList + if err := k8stools.ListObjectsByNamespace(ctx, r.Client, r.BaseConf.WatchNamespaces, func(dst *vmv1beta1.VMAgentList) { + objects.Items = append(objects.Items, dst.Items...) + }); err != nil { + return result, fmt.Errorf("cannot list VMAgents for VMStaticScrape: %w", err) } - // only check selector when deleting object, - // since labels can be changed when updating and we can't tell if it was selected before, and we can't tell if it's creating or updating. - if !instance.DeletionTimestamp.IsZero() { - opts := &k8stools.SelectorOpts{ - SelectAll: item.Spec.SelectAllByDefault, - NamespaceSelector: item.Spec.StaticScrapeNamespaceSelector, - ObjectSelector: item.Spec.StaticScrapeSelector, - DefaultNamespace: instance.Namespace, + + for i := range objects.Items { + item := &objects.Items[i] + if !item.DeletionTimestamp.IsZero() || item.Spec.ParsingError != "" || item.IsStaticScrapeUnmanaged() { + continue } - match, err := isSelectorsMatchesTargetCRD(ctx, r.Client, instance, item, opts) - if err != nil { - l.Error(err, "cannot match vmagent and vmstaticscrape") + l := l.WithValues("vmagent", item.Name, "parent_namespace", item.Namespace) + ctx := logger.AddToContext(ctx, l) + if item.Spec.DaemonSetMode { continue } - if !match { + // only check selector when deleting object, + // since labels can be changed when updating and we can't tell if it was selected before, and we can't tell if it's creating or updating. + if !instance.DeletionTimestamp.IsZero() { + opts := &k8stools.SelectorOpts{ + SelectAll: item.Spec.SelectAllByDefault, + NamespaceSelector: item.Spec.StaticScrapeNamespaceSelector, + ObjectSelector: item.Spec.StaticScrapeSelector, + DefaultNamespace: instance.Namespace, + } + match, err := isSelectorsMatchesTargetCRD(ctx, r.Client, instance, item, opts) + if err != nil { + l.Error(err, "cannot match VMAgent and VMStaticScrape") + continue + } + if !match { + continue + } + } + + if err := vmagent.CreateOrUpdateScrapeConfig(ctx, r, item, instance); err != nil { continue } } + agentSync.Unlock() + } + + if !vmsingleReconcileLimit.MustThrottleReconcile() { + vmsingleSync.Lock() + + var objects vmv1beta1.VMSingleList + if err := k8stools.ListObjectsByNamespace(ctx, r.Client, r.BaseConf.WatchNamespaces, func(dst *vmv1beta1.VMSingleList) { + objects.Items = append(objects.Items, dst.Items...) + }); err != nil { + return result, fmt.Errorf("cannot list VMSingles for VMStaticScrape: %w", err) + } + + for i := range objects.Items { + item := &objects.Items[i] + if !item.DeletionTimestamp.IsZero() || item.Spec.ParsingError != "" || item.IsStaticScrapeUnmanaged() { + continue + } + l := l.WithValues("vmsingle", item.Name, "parent_namespace", item.Namespace) + ctx := logger.AddToContext(ctx, l) + // only check selector when deleting object, + // since labels can be changed when updating and we can't tell if it was selected before, and we can't tell if it's creating or updating. + if !instance.DeletionTimestamp.IsZero() { + opts := &k8stools.SelectorOpts{ + SelectAll: item.Spec.SelectAllByDefault, + NamespaceSelector: item.Spec.StaticScrapeNamespaceSelector, + ObjectSelector: item.Spec.StaticScrapeSelector, + DefaultNamespace: instance.Namespace, + } + match, err := isSelectorsMatchesTargetCRD(ctx, r.Client, instance, item, opts) + if err != nil { + l.Error(err, "cannot match VMSingle and VMStaticScrape") + continue + } + if !match { + continue + } + } - if err := vmagent.CreateOrUpdateScrapeConfig(ctx, r, item, instance); err != nil { - continue + if err := vmsingle.CreateOrUpdateScrapeConfig(ctx, r, item, instance); err != nil { + continue + } } + vmsingleSync.Unlock() } + return } diff --git a/test/e2e/vmagent_test.go b/test/e2e/vmagent_test.go index 4b2415343..8bc07fcff 100644 --- a/test/e2e/vmagent_test.go +++ b/test/e2e/vmagent_test.go @@ -494,7 +494,7 @@ var _ = Describe("test vmagent Controller", Label("vm", "agent", "vmagent"), fun cr.Spec.ReplicaCount = ptr.To[int32](1) cr.Spec.ShardCount = ptr.To(2) cr.Spec.StatefulMode = true - cr.Spec.IngestOnlyMode = true + cr.Spec.IngestOnlyMode = ptr.To(true) }, verify: func(cr *vmv1beta1.VMAgent) { var createdSts appsv1.StatefulSet diff --git a/test/e2e/vmalertmanager_test.go b/test/e2e/vmalertmanager_test.go index aa253e56e..ab7eaf561 100644 --- a/test/e2e/vmalertmanager_test.go +++ b/test/e2e/vmalertmanager_test.go @@ -2,7 +2,6 @@ package e2e import ( "bytes" - "compress/gzip" "context" "fmt" @@ -15,6 +14,7 @@ import ( "k8s.io/utils/ptr" 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/finalize" ) @@ -37,15 +37,6 @@ receivers: ` ) -func gzipConfig(buf *bytes.Buffer, conf []byte) error { - w := gzip.NewWriter(buf) - defer w.Close() - if _, err := w.Write(conf); err != nil { - return err - } - return nil -} - //nolint:dupl var _ = Describe("test vmalertmanager Controller", Label("vm", "alertmanager"), func() { @@ -207,7 +198,7 @@ var _ = Describe("test vmalertmanager Controller", Label("vm", "alertmanager"), func(cr *vmv1beta1.VMAlertmanager) { var amCfg corev1.Secret var buf bytes.Buffer - Expect(gzipConfig(&buf, []byte(alertmanagerTestConf))).To(Succeed()) + Expect(build.GzipConfig(&buf, []byte(alertmanagerTestConf))).To(Succeed()) Expect(k8sClient.Get(ctx, types.NamespacedName{Name: cr.ConfigSecretName(), Namespace: namespace}, &amCfg)).To(Succeed()) Expect(amCfg.Data["alertmanager.yaml.gz"]).To(Equal(buf.Bytes()))