diff --git a/api/monitoring/v1/groupversion_info.go b/api/monitoring/v1/groupversion_info.go new file mode 100755 index 00000000..e5ab3884 --- /dev/null +++ b/api/monitoring/v1/groupversion_info.go @@ -0,0 +1,20 @@ +// Package v1 contains API Schema definitions for the infrastructure v1 API group +// +kubebuilder:object:generate=true +// +groupName=monitoring.coreos.com +package v1 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +var ( + // GroupVersion is group version used to register these objects + GroupVersion = schema.GroupVersion{Group: "monitoring.coreos.com", Version: "v1"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} + + // AddToScheme adds the types in this group-version to the given scheme. + AddToScheme = SchemeBuilder.AddToScheme +) diff --git a/api/monitoring/v1/monitor_types.go b/api/monitoring/v1/monitor_types.go new file mode 100755 index 00000000..77f11169 --- /dev/null +++ b/api/monitoring/v1/monitor_types.go @@ -0,0 +1,133 @@ +package v1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" +) + +const ( + Version = "v1" + + PodMonitorsKind = "PodMonitor" + PodMonitorName = "podmonitors" + PodMonitorKindKey = "podmonitor" +) + +// PodMonitor defines monitoring for a set of pods. +// +genclient +// +k8s:openapi-gen=true + +// +kubebuilder:object:root=true +type PodMonitor struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + // Specification of desired Pod selection for target discovery by Prometheus. + Spec PodMonitorSpec `json:"spec"` +} + +// PodMonitorSpec contains specification parameters for a PodMonitor. +// +k8s:openapi-gen=true +type PodMonitorSpec struct { + // The label to use to retrieve the job name from. + JobLabel string `json:"jobLabel,omitempty"` + // PodTargetLabels transfers labels on the Kubernetes Pod onto the target. + PodTargetLabels []string `json:"podTargetLabels,omitempty"` + // A list of endpoints allowed as part of this PodMonitor. + PodMetricsEndpoints []PodMetricsEndpoint `json:"podMetricsEndpoints"` + // Selector to select Pod objects. + Selector metav1.LabelSelector `json:"selector"` + // Selector to select which namespaces the Endpoints objects are discovered from. + NamespaceSelector NamespaceSelector `json:"namespaceSelector,omitempty"` + // SampleLimit defines per-scrape limit on number of scraped samples that will be accepted. + SampleLimit uint64 `json:"sampleLimit,omitempty"` +} + +// PodMonitorList is a list of PodMonitors. +// +k8s:openapi-gen=true + +// +kubebuilder:object:root=true +type PodMonitorList struct { + metav1.TypeMeta `json:",inline"` + // Standard list metadata + // More info: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#metadata + metav1.ListMeta `json:"metadata,omitempty"` + // List of PodMonitors + Items []*PodMonitor `json:"items"` +} + +// RelabelConfig allows dynamic rewriting of the label set, being applied to samples before ingestion. +// It defines ``-section of Prometheus configuration. +// More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#metric_relabel_configs +// +k8s:openapi-gen=true +type RelabelConfig struct { + //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. + SourceLabels []string `json:"sourceLabels,omitempty"` + //Separator placed between concatenated source label values. default is ';'. + Separator string `json:"separator,omitempty"` + //Label to which the resulting value is written in a replace action. + //It is mandatory for replace actions. Regex capture groups are available. + TargetLabel string `json:"targetLabel,omitempty"` + //Regular expression against which the extracted value is matched. Default is '(.*)' + Regex string `json:"regex,omitempty"` + // Modulus to take of the hash of the source label values. + Modulus uint64 `json:"modulus,omitempty"` + //Replacement value against which a regex replace is performed if the + //regular expression matches. Regex capture groups are available. Default is '$1' + Replacement string `json:"replacement,omitempty"` + // Action to perform based on regex matching. Default is 'replace' + Action string `json:"action,omitempty"` +} + +// PodMetricsEndpoint defines a scrapeable endpoint of a Kubernetes Pod serving Prometheus metrics. +// +k8s:openapi-gen=true +type PodMetricsEndpoint struct { + // Name of the pod port this endpoint refers to. Mutually exclusive with targetPort. + Port string `json:"port,omitempty"` + // Deprecated: Use 'port' instead. + TargetPort *intstr.IntOrString `json:"targetPort,omitempty"` + // HTTP path to scrape for metrics. + Path string `json:"path,omitempty"` + // HTTP scheme to use for scraping. + Scheme string `json:"scheme,omitempty"` + // Optional HTTP URL parameters + Params map[string][]string `json:"params,omitempty"` + // Interval at which metrics should be scraped + Interval string `json:"interval,omitempty"` + // Timeout after which the scrape is ended + ScrapeTimeout string `json:"scrapeTimeout,omitempty"` + // HonorLabels chooses the metric's labels on collisions with target labels. + HonorLabels bool `json:"honorLabels,omitempty"` + // HonorTimestamps controls whether Prometheus respects the timestamps present in scraped data. + HonorTimestamps *bool `json:"honorTimestamps,omitempty"` + // MetricRelabelConfigs to apply to samples before ingestion. + MetricRelabelConfigs []*RelabelConfig `json:"metricRelabelings,omitempty"` + // RelabelConfigs to apply to samples before ingestion. + // More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config + RelabelConfigs []*RelabelConfig `json:"relabelings,omitempty"` + // ProxyURL eg http://proxyserver:2195 Directs scrapes to proxy through this endpoint. + ProxyURL *string `json:"proxyUrl,omitempty"` +} + +// NamespaceSelector is a selector for selecting either all namespaces or a +// list of namespaces. +// +k8s:openapi-gen=true +type NamespaceSelector struct { + // Boolean describing whether all namespaces are selected in contrast to a + // list restricting them. + Any bool `json:"any,omitempty"` + // List of namespace names. + MatchNames []string `json:"matchNames,omitempty"` + + // TODO(fabxc): this should embed metav1.LabelSelector eventually. + // Currently the selector is only used for namespaces which require more complex + // implementation to support label selections. +} + +// +kubebuilder:rbac:groups=monitoring.coreos.com,resources=podmonitors,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=monitoring.coreos.com,resources=podmonitors/status,verbs=get;update;patch + +func init() { + SchemeBuilder.Register(&PodMonitor{}, &PodMonitorList{}) +} diff --git a/api/monitoring/v1/zz_generated.deepcopy.go b/api/monitoring/v1/zz_generated.deepcopy.go new file mode 100755 index 00000000..8bae4be0 --- /dev/null +++ b/api/monitoring/v1/zz_generated.deepcopy.go @@ -0,0 +1,224 @@ +// +build !ignore_autogenerated + +/* + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package v1 + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/intstr" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NamespaceSelector) DeepCopyInto(out *NamespaceSelector) { + *out = *in + if in.MatchNames != nil { + in, out := &in.MatchNames, &out.MatchNames + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NamespaceSelector. +func (in *NamespaceSelector) DeepCopy() *NamespaceSelector { + if in == nil { + return nil + } + out := new(NamespaceSelector) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PodMetricsEndpoint) DeepCopyInto(out *PodMetricsEndpoint) { + *out = *in + if in.TargetPort != nil { + in, out := &in.TargetPort, &out.TargetPort + *out = new(intstr.IntOrString) + **out = **in + } + if in.Params != nil { + in, out := &in.Params, &out.Params + *out = make(map[string][]string, len(*in)) + for key, val := range *in { + var outVal []string + if val == nil { + (*out)[key] = nil + } else { + in, out := &val, &outVal + *out = make([]string, len(*in)) + copy(*out, *in) + } + (*out)[key] = outVal + } + } + if in.HonorTimestamps != nil { + in, out := &in.HonorTimestamps, &out.HonorTimestamps + *out = new(bool) + **out = **in + } + if in.MetricRelabelConfigs != nil { + in, out := &in.MetricRelabelConfigs, &out.MetricRelabelConfigs + *out = make([]*RelabelConfig, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(RelabelConfig) + (*in).DeepCopyInto(*out) + } + } + } + if in.RelabelConfigs != nil { + in, out := &in.RelabelConfigs, &out.RelabelConfigs + *out = make([]*RelabelConfig, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(RelabelConfig) + (*in).DeepCopyInto(*out) + } + } + } + if in.ProxyURL != nil { + in, out := &in.ProxyURL, &out.ProxyURL + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodMetricsEndpoint. +func (in *PodMetricsEndpoint) DeepCopy() *PodMetricsEndpoint { + if in == nil { + return nil + } + out := new(PodMetricsEndpoint) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PodMonitor) DeepCopyInto(out *PodMonitor) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodMonitor. +func (in *PodMonitor) DeepCopy() *PodMonitor { + if in == nil { + return nil + } + out := new(PodMonitor) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PodMonitor) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PodMonitorList) DeepCopyInto(out *PodMonitorList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]*PodMonitor, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(PodMonitor) + (*in).DeepCopyInto(*out) + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodMonitorList. +func (in *PodMonitorList) DeepCopy() *PodMonitorList { + if in == nil { + return nil + } + out := new(PodMonitorList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PodMonitorList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PodMonitorSpec) DeepCopyInto(out *PodMonitorSpec) { + *out = *in + if in.PodTargetLabels != nil { + in, out := &in.PodTargetLabels, &out.PodTargetLabels + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.PodMetricsEndpoints != nil { + in, out := &in.PodMetricsEndpoints, &out.PodMetricsEndpoints + *out = make([]PodMetricsEndpoint, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + in.Selector.DeepCopyInto(&out.Selector) + in.NamespaceSelector.DeepCopyInto(&out.NamespaceSelector) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodMonitorSpec. +func (in *PodMonitorSpec) DeepCopy() *PodMonitorSpec { + if in == nil { + return nil + } + out := new(PodMonitorSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RelabelConfig) DeepCopyInto(out *RelabelConfig) { + *out = *in + if in.SourceLabels != nil { + in, out := &in.SourceLabels, &out.SourceLabels + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RelabelConfig. +func (in *RelabelConfig) DeepCopy() *RelabelConfig { + if in == nil { + return nil + } + out := new(RelabelConfig) + in.DeepCopyInto(out) + return out +} diff --git a/api/v1alpha1/groupversion_info.go b/api/v1alpha1/groupversion_info.go old mode 100644 new mode 100755 diff --git a/api/v1alpha1/mcrouter_types.go b/api/v1alpha1/mcrouter_types.go old mode 100644 new mode 100755 diff --git a/api/v1alpha1/memcached_types.go b/api/v1alpha1/memcached_types.go old mode 100644 new mode 100755 diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go old mode 100644 new mode 100755 diff --git a/builders/pod_metrics_endpoint.go b/builders/pod_metrics_endpoint.go new file mode 100755 index 00000000..5e1426fb --- /dev/null +++ b/builders/pod_metrics_endpoint.go @@ -0,0 +1,68 @@ +package builders + +import ( + "k8s.io/apimachinery/pkg/util/intstr" + monitoringv1 "opendev.org/vexxhost/openstack-operator/api/monitoring/v1" +) + +// PodMetricsEndpointBuilder provides an interface to build podmonitors +type PodMetricsEndpointBuilder struct { + obj *monitoringv1.PodMetricsEndpoint +} + +// PodMonitor returns a new podmonitor builder +func PodMetricsEndpoint() *PodMetricsEndpointBuilder { + podMetricsEndpoint := &monitoringv1.PodMetricsEndpoint{} + return &PodMetricsEndpointBuilder{ + obj: podMetricsEndpoint, + } +} + +func (pme *PodMetricsEndpointBuilder) Port(port string) *PodMetricsEndpointBuilder { + pme.obj.Port = port + return pme +} + +func (pme *PodMetricsEndpointBuilder) TargetPort(targetPort intstr.IntOrString) *PodMetricsEndpointBuilder { + pme.obj.TargetPort = &targetPort + return pme +} +func (pme *PodMetricsEndpointBuilder) Path(path string) *PodMetricsEndpointBuilder { + pme.obj.Path = path + return pme +} +func (pme *PodMetricsEndpointBuilder) Scheme(scheme string) *PodMetricsEndpointBuilder { + pme.obj.Scheme = scheme + return pme +} +func (pme *PodMetricsEndpointBuilder) Params(params map[string][]string) *PodMetricsEndpointBuilder { + pme.obj.Params = params + return pme +} + +func (pme *PodMetricsEndpointBuilder) Interval(interval string) *PodMetricsEndpointBuilder { + pme.obj.Interval = interval + return pme +} +func (pme *PodMetricsEndpointBuilder) ScrapeTimeout(scrapeTimeout string) *PodMetricsEndpointBuilder { + pme.obj.ScrapeTimeout = scrapeTimeout + return pme +} +func (pme *PodMetricsEndpointBuilder) HonorLabels(honorLabels bool) *PodMetricsEndpointBuilder { + pme.obj.HonorLabels = honorLabels + return pme +} +func (pme *PodMetricsEndpointBuilder) HonorTimestamps(honorTimestamps bool) *PodMetricsEndpointBuilder { + pme.obj.HonorTimestamps = &honorTimestamps + return pme +} + +func (pme *PodMetricsEndpointBuilder) ProxyURL(proxyURL string) *PodMetricsEndpointBuilder { + pme.obj.ProxyURL = &proxyURL + return pme +} + +// Build returns the object after making certain assertions +func (pme *PodMetricsEndpointBuilder) Build() (monitoringv1.PodMetricsEndpoint, error) { + return *pme.obj, nil +} diff --git a/builders/pod_monitor.go b/builders/pod_monitor.go new file mode 100755 index 00000000..064971bd --- /dev/null +++ b/builders/pod_monitor.go @@ -0,0 +1,92 @@ +package builders + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + monitoringv1 "opendev.org/vexxhost/openstack-operator/api/monitoring/v1" + + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" +) + +// PodMonitorBuilder provides an interface to build podmonitors +type PodMonitorBuilder struct { + obj *monitoringv1.PodMonitor + podMetricsEndpoints []*PodMetricsEndpointBuilder + owner metav1.Object + scheme *runtime.Scheme +} + +// PodMonitor returns a new podmonitor builder +func PodMonitor(existing *monitoringv1.PodMonitor, owner metav1.Object, scheme *runtime.Scheme) *PodMonitorBuilder { + return &PodMonitorBuilder{ + obj: existing, + owner: owner, + scheme: scheme, + } +} + +func (pm *PodMonitorBuilder) Selector(matchLabels map[string]string) *PodMonitorBuilder { + pm.obj.Spec.Selector = metav1.LabelSelector{ + MatchLabels: matchLabels, + } + return pm +} + +func (pm *PodMonitorBuilder) PodTargetLabels(podTargetLabels []string) *PodMonitorBuilder { + pm.obj.Spec.PodTargetLabels = podTargetLabels + return pm +} +func (pm *PodMonitorBuilder) JobLabel(jobLabel string) *PodMonitorBuilder { + pm.obj.Spec.JobLabel = jobLabel + return pm +} +func (pm *PodMonitorBuilder) NamespaceSelector(any bool, matchNames []string) *PodMonitorBuilder { + pm.obj.Spec.NamespaceSelector = monitoringv1.NamespaceSelector{ + Any: any, + MatchNames: matchNames, + } + + return pm +} +func (pm *PodMonitorBuilder) SampleLimit(sampleLimit uint64) *PodMonitorBuilder { + pm.obj.Spec.SampleLimit = sampleLimit + return pm +} + +func (pm *PodMonitorBuilder) PodMetricsEndpoints(pme ...*PodMetricsEndpointBuilder) *PodMonitorBuilder { + pm.podMetricsEndpoints = pme + return pm +} + +// Build returns the object after making certain assertions +func (pm *PodMonitorBuilder) Build() error { + pm.obj.Spec.PodMetricsEndpoints = []monitoringv1.PodMetricsEndpoint{} + for _, pmeBuilder := range pm.podMetricsEndpoints { + podMetricsEndpoint, err := pmeBuilder.Build() + if err != nil { + return err + } + + pm.obj.Spec.PodMetricsEndpoints = append(pm.obj.Spec.PodMetricsEndpoints, podMetricsEndpoint) + } + + if !pm.isOwnedByOthers() { + return controllerutil.SetControllerReference(pm.owner, pm.obj, pm.scheme) + } + return nil +} + +// isOwnedByOthers checks if this podMonitor has been possessed by an another object already. +func (pm *PodMonitorBuilder) isOwnedByOthers() bool { + ownerName := pm.owner.GetName() + + existingRefs := pm.obj.GetOwnerReferences() + for _, r := range existingRefs { + if r.Name == ownerName { + return false + } else if r.Controller != nil && *r.Controller { + return true + } + } + return false +} diff --git a/chart/crds/monitoring.coreos.com_podmonitors.yaml b/chart/crds/monitoring.coreos.com_podmonitors.yaml new file mode 100644 index 00000000..59706ba6 --- /dev/null +++ b/chart/crds/monitoring.coreos.com_podmonitors.yaml @@ -0,0 +1,265 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.2.5 + creationTimestamp: null + name: podmonitors.monitoring.coreos.com +spec: + group: monitoring.coreos.com + names: + kind: PodMonitor + listKind: PodMonitorList + plural: podmonitors + singular: podmonitor + scope: Namespaced + validation: + openAPIV3Schema: + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Specification of desired Pod selection for target discovery + by Prometheus. + properties: + jobLabel: + description: The label to use to retrieve the job name from. + type: string + namespaceSelector: + description: Selector to select which namespaces the Endpoints objects + are discovered from. + properties: + any: + description: Boolean describing whether all namespaces are selected + in contrast to a list restricting them. + type: boolean + matchNames: + description: List of namespace names. + items: + type: string + type: array + type: object + podMetricsEndpoints: + description: A list of endpoints allowed as part of this PodMonitor. + items: + description: PodMetricsEndpoint defines a scrapeable endpoint of a + Kubernetes Pod serving Prometheus metrics. + properties: + honorLabels: + description: HonorLabels chooses the metric's labels on collisions + with target labels. + type: boolean + honorTimestamps: + description: HonorTimestamps controls whether Prometheus respects + the timestamps present in scraped data. + type: boolean + interval: + description: Interval at which metrics should be scraped + type: string + metricRelabelings: + description: MetricRelabelConfigs to apply to samples before ingestion. + items: + description: 'RelabelConfig allows dynamic rewriting of the + label set, being applied to samples before ingestion. It defines + ``-section of Prometheus configuration. + More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#metric_relabel_configs' + properties: + action: + description: Action to perform based on regex matching. + Default is 'replace' + 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 '(.*)' + type: string + 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 + 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 + 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 + params: + additionalProperties: + items: + type: string + type: array + description: Optional HTTP URL parameters + type: object + path: + description: HTTP path to scrape for metrics. + type: string + port: + description: Name of the pod port this endpoint refers to. Mutually + exclusive with targetPort. + type: string + proxyUrl: + description: ProxyURL eg http://proxyserver:2195 Directs scrapes + to proxy through this endpoint. + type: string + relabelings: + description: 'RelabelConfigs to apply to samples before ingestion. + More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config' + items: + description: 'RelabelConfig allows dynamic rewriting of the + label set, being applied to samples before ingestion. It defines + ``-section of Prometheus configuration. + More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#metric_relabel_configs' + properties: + action: + description: Action to perform based on regex matching. + Default is 'replace' + 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 '(.*)' + type: string + 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 + 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 + 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 + scheme: + description: HTTP scheme to use for scraping. + type: string + scrapeTimeout: + description: Timeout after which the scrape is ended + type: string + targetPort: + anyOf: + - type: integer + - type: string + description: 'Deprecated: Use ''port'' instead.' + x-kubernetes-int-or-string: true + type: object + type: array + podTargetLabels: + description: PodTargetLabels transfers labels on the Kubernetes Pod + onto the target. + items: + type: string + type: array + sampleLimit: + description: SampleLimit defines per-scrape limit on number of scraped + samples that will be accepted. + format: int64 + type: integer + selector: + description: Selector to select Pod objects. + 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 + required: + - key + - operator + type: object + type: array + 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 + required: + - podMetricsEndpoints + - selector + type: object + required: + - spec + type: object + version: v1 + versions: + - name: v1 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/chart/templates/clusterrole.yaml b/chart/templates/clusterrole.yaml index 200c1e17..7d5ce46c 100644 --- a/chart/templates/clusterrole.yaml +++ b/chart/templates/clusterrole.yaml @@ -83,3 +83,23 @@ rules: - get - patch - update +- apiGroups: + - monitoring.coreos.com + resources: + - podmonitors + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - monitoring.coreos.com + resources: + - podmonitors/status + verbs: + - get + - patch + - update diff --git a/config/crd/bases/monitoring.coreos.com_podmonitors.yaml b/config/crd/bases/monitoring.coreos.com_podmonitors.yaml new file mode 100644 index 00000000..59706ba6 --- /dev/null +++ b/config/crd/bases/monitoring.coreos.com_podmonitors.yaml @@ -0,0 +1,265 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.2.5 + creationTimestamp: null + name: podmonitors.monitoring.coreos.com +spec: + group: monitoring.coreos.com + names: + kind: PodMonitor + listKind: PodMonitorList + plural: podmonitors + singular: podmonitor + scope: Namespaced + validation: + openAPIV3Schema: + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Specification of desired Pod selection for target discovery + by Prometheus. + properties: + jobLabel: + description: The label to use to retrieve the job name from. + type: string + namespaceSelector: + description: Selector to select which namespaces the Endpoints objects + are discovered from. + properties: + any: + description: Boolean describing whether all namespaces are selected + in contrast to a list restricting them. + type: boolean + matchNames: + description: List of namespace names. + items: + type: string + type: array + type: object + podMetricsEndpoints: + description: A list of endpoints allowed as part of this PodMonitor. + items: + description: PodMetricsEndpoint defines a scrapeable endpoint of a + Kubernetes Pod serving Prometheus metrics. + properties: + honorLabels: + description: HonorLabels chooses the metric's labels on collisions + with target labels. + type: boolean + honorTimestamps: + description: HonorTimestamps controls whether Prometheus respects + the timestamps present in scraped data. + type: boolean + interval: + description: Interval at which metrics should be scraped + type: string + metricRelabelings: + description: MetricRelabelConfigs to apply to samples before ingestion. + items: + description: 'RelabelConfig allows dynamic rewriting of the + label set, being applied to samples before ingestion. It defines + ``-section of Prometheus configuration. + More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#metric_relabel_configs' + properties: + action: + description: Action to perform based on regex matching. + Default is 'replace' + 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 '(.*)' + type: string + 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 + 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 + 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 + params: + additionalProperties: + items: + type: string + type: array + description: Optional HTTP URL parameters + type: object + path: + description: HTTP path to scrape for metrics. + type: string + port: + description: Name of the pod port this endpoint refers to. Mutually + exclusive with targetPort. + type: string + proxyUrl: + description: ProxyURL eg http://proxyserver:2195 Directs scrapes + to proxy through this endpoint. + type: string + relabelings: + description: 'RelabelConfigs to apply to samples before ingestion. + More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config' + items: + description: 'RelabelConfig allows dynamic rewriting of the + label set, being applied to samples before ingestion. It defines + ``-section of Prometheus configuration. + More info: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#metric_relabel_configs' + properties: + action: + description: Action to perform based on regex matching. + Default is 'replace' + 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 '(.*)' + type: string + 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 + 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 + 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 + scheme: + description: HTTP scheme to use for scraping. + type: string + scrapeTimeout: + description: Timeout after which the scrape is ended + type: string + targetPort: + anyOf: + - type: integer + - type: string + description: 'Deprecated: Use ''port'' instead.' + x-kubernetes-int-or-string: true + type: object + type: array + podTargetLabels: + description: PodTargetLabels transfers labels on the Kubernetes Pod + onto the target. + items: + type: string + type: array + sampleLimit: + description: SampleLimit defines per-scrape limit on number of scraped + samples that will be accepted. + format: int64 + type: integer + selector: + description: Selector to select Pod objects. + 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 + required: + - key + - operator + type: object + type: array + 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 + required: + - podMetricsEndpoints + - selector + type: object + required: + - spec + type: object + version: v1 + versions: + - name: v1 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index d1f8f26f..bdc18be0 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -4,6 +4,7 @@ resources: - bases/infrastructure.vexxhost.cloud_mcrouters.yaml - bases/infrastructure.vexxhost.cloud_memcacheds.yaml +- bases/monitoring.coreos.com_podmonitors.yaml # +kubebuilder:scaffold:crdkustomizeresource patchesStrategicMerge: diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index a54381c2..c2d3da4b 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -83,3 +83,23 @@ rules: - get - patch - update +- apiGroups: + - monitoring.coreos.com + resources: + - podmonitors + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - monitoring.coreos.com + resources: + - podmonitors/status + verbs: + - get + - patch + - update diff --git a/controllers/mcrouter_controller.go b/controllers/mcrouter_controller.go index bf2c908d..1f44abee 100755 --- a/controllers/mcrouter_controller.go +++ b/controllers/mcrouter_controller.go @@ -13,6 +13,7 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" + monitoringv1 "opendev.org/vexxhost/openstack-operator/api/monitoring/v1" infrastructurev1alpha1 "opendev.org/vexxhost/openstack-operator/api/v1alpha1" "opendev.org/vexxhost/openstack-operator/builders" "opendev.org/vexxhost/openstack-operator/utils" @@ -28,6 +29,7 @@ type McrouterReconciler struct { // +kubebuilder:rbac:groups=infrastructure.vexxhost.cloud,resources=mcrouters,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=infrastructure.vexxhost.cloud,resources=mcrouters/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=monitoring.coreos.com,resources=podmonitors,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=core,resources=configmaps;services,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete @@ -118,6 +120,39 @@ func (r *McrouterReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) { } log.WithValues("resource", "Deployment").WithValues("op", op).Info("Reconciled") + // PodMonitor + podMonitor := &monitoringv1.PodMonitor{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "monitoring.coreos.com/v1", + Kind: "PodMonitor", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: req.Namespace, + Name: fmt.Sprintf("mcrouter-podmonitor"), + Labels: map[string]string{ + "app.kubernetes.io/name": "mcrouter", + }, + }, + } + + op, err = utils.CreateOrUpdate(ctx, r, podMonitor, func() error { + return builders.PodMonitor(podMonitor, &mcrouter, r.Scheme). + Selector(map[string]string{ + "app.kubernetes.io/name": "mcrouter", + }). + PodMetricsEndpoints( + builders.PodMetricsEndpoint(). + Port("metrics"). + Path("/metrics"). + Interval("15s"), + ).Build() + + }) + if err != nil { + return ctrl.Result{}, err + } + log.WithValues("resource", "podmonitor").WithValues("op", op).Info("Reconciled") + // Service service := &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ @@ -146,5 +181,6 @@ func (r *McrouterReconciler) SetupWithManager(mgr ctrl.Manager) error { Owns(&corev1.ConfigMap{}). Owns(&appsv1.Deployment{}). Owns(&corev1.Service{}). + Owns(&monitoringv1.PodMonitor{}). Complete(r) } diff --git a/controllers/memcached_controller.go b/controllers/memcached_controller.go index 544d7099..2c345b7a 100755 --- a/controllers/memcached_controller.go +++ b/controllers/memcached_controller.go @@ -32,6 +32,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" infrastructurev1alpha1 "opendev.org/vexxhost/openstack-operator/api/v1alpha1" + + monitoringv1 "opendev.org/vexxhost/openstack-operator/api/monitoring/v1" "opendev.org/vexxhost/openstack-operator/builders" "opendev.org/vexxhost/openstack-operator/utils" ) @@ -46,6 +48,7 @@ type MemcachedReconciler struct { // +kubebuilder:rbac:groups=infrastructure.vexxhost.cloud,resources=memcacheds,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=infrastructure.vexxhost.cloud,resources=memcacheds/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=monitoring.coreos.com,resources=podmonitors,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete @@ -76,6 +79,7 @@ func (r *MemcachedReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) { Labels: labels, }, } + op, err := utils.CreateOrUpdate(ctx, r, deployment, func() error { return builders.Deployment(deployment, &memcached, r.Scheme). Labels(labels). @@ -113,6 +117,40 @@ func (r *MemcachedReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) { } log.WithValues("resource", "Deployment").WithValues("op", op).Info("Reconciled") + // PodMonitor + + podMonitor := &monitoringv1.PodMonitor{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "monitoring.coreos.com/v1", + Kind: "PodMonitor", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: req.Namespace, + Name: fmt.Sprintf("memcached-podmonitor"), + Labels: map[string]string{ + "app.kubernetes.io/name": "memcached", + }, + }, + } + + op, err = utils.CreateOrUpdate(ctx, r, podMonitor, func() error { + return builders.PodMonitor(podMonitor, &memcached, r.Scheme). + Selector(map[string]string{ + "app.kubernetes.io/name": "memcached", + }). + PodMetricsEndpoints( + builders.PodMetricsEndpoint(). + Port("metrics"). + Path("/metrics"). + Interval("15s"), + ).Build() + + }) + if err != nil { + return ctrl.Result{}, err + } + log.WithValues("resource", "podmonitor").WithValues("op", op).Info("Reconciled") + // Pods pods := &corev1.PodList{} err = r.List(ctx, pods, client.InNamespace(req.Namespace), client.MatchingLabels(labels)) @@ -175,5 +213,6 @@ func (r *MemcachedReconciler) SetupWithManager(mgr ctrl.Manager) error { For(&infrastructurev1alpha1.Memcached{}). Owns(&appsv1.Deployment{}). Owns(&infrastructurev1alpha1.Mcrouter{}). + Owns(&monitoringv1.PodMonitor{}). Complete(r) } diff --git a/main.go b/main.go old mode 100644 new mode 100755 index 573c63cf..9c4553c5 --- a/main.go +++ b/main.go @@ -26,6 +26,7 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/log/zap" + monitoringv1 "opendev.org/vexxhost/openstack-operator/api/monitoring/v1" infrastructurev1alpha1 "opendev.org/vexxhost/openstack-operator/api/v1alpha1" "opendev.org/vexxhost/openstack-operator/controllers" "opendev.org/vexxhost/openstack-operator/version" @@ -41,6 +42,7 @@ func init() { _ = clientgoscheme.AddToScheme(scheme) _ = infrastructurev1alpha1.AddToScheme(scheme) + _ = monitoringv1.AddToScheme(scheme) // +kubebuilder:scaffold:scheme } diff --git a/utils/kubernetes.go b/utils/kubernetes.go index f8f63c5f..e778a6ae 100644 --- a/utils/kubernetes.go +++ b/utils/kubernetes.go @@ -13,6 +13,7 @@ import ( // CreateOrUpdate wraps the function provided by controller-runtime to include // some additional logging and common functionality across all resources. func CreateOrUpdate(ctx context.Context, c client.Client, obj runtime.Object, f controllerutil.MutateFn) (controllerutil.OperationResult, error) { + return controllerutil.CreateOrUpdate(ctx, c, obj, func() error { original := obj.DeepCopyObject()