From f81ef338f0cad8d2d00dffa49fbb3b72d1157a6d Mon Sep 17 00:00:00 2001 From: Jacob Aronoff Date: Fri, 20 Sep 2024 09:40:43 -0400 Subject: [PATCH 1/4] Prep release for 0.109.0 (#3295) --- .chloggen/3218-align-label-filter-flag.yaml | 16 ------------- .chloggen/bug-fix-hpa.yaml | 16 ------------- .chloggen/enhanced-webhook.yaml | 16 ------------- CHANGELOG.md | 23 +++++++++++++++++++ README.md | 1 + RELEASE.md | 2 +- ...emetry-operator.clusterserviceversion.yaml | 8 +++---- ...emetry-operator.clusterserviceversion.yaml | 8 +++---- versions.txt | 8 +++---- 9 files changed, 37 insertions(+), 61 deletions(-) delete mode 100755 .chloggen/3218-align-label-filter-flag.yaml delete mode 100755 .chloggen/bug-fix-hpa.yaml delete mode 100644 .chloggen/enhanced-webhook.yaml diff --git a/.chloggen/3218-align-label-filter-flag.yaml b/.chloggen/3218-align-label-filter-flag.yaml deleted file mode 100755 index 3daf4a7cf1..0000000000 --- a/.chloggen/3218-align-label-filter-flag.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' -change_type: deprecation - -# The name of the component, or a single word describing the area of concern, (e.g. collector, target allocator, auto-instrumentation, opamp, github action) -component: operator - -# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). -note: "Deprecated `label` flag and introduced `labels-filter` flag to align the label filtering with the attribute filtering flag name. The `label` flag will be removed when #3236 issue is resolved." - -# One or more tracking issues related to the change -issues: [3218] - -# (Optional) One or more lines of additional information to render under the primary note. -# These lines will be padded with 2 spaces and then inserted directly into the document. -# Use pipe (|) for multiline entries. -subtext: diff --git a/.chloggen/bug-fix-hpa.yaml b/.chloggen/bug-fix-hpa.yaml deleted file mode 100755 index adc23e56a6..0000000000 --- a/.chloggen/bug-fix-hpa.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' -change_type: enhancement - -# The name of the component, or a single word describing the area of concern, (e.g. collector, target allocator, auto-instrumentation, opamp, github action) -component: collector - -# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). -note: adds test for memory utilization - -# One or more tracking issues related to the change -issues: [3283] - -# (Optional) One or more lines of additional information to render under the primary note. -# These lines will be padded with 2 spaces and then inserted directly into the document. -# Use pipe (|) for multiline entries. -subtext: diff --git a/.chloggen/enhanced-webhook.yaml b/.chloggen/enhanced-webhook.yaml deleted file mode 100644 index ddacb9ee1a..0000000000 --- a/.chloggen/enhanced-webhook.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' -change_type: enhancement - -# The name of the component, or a single word describing the area of concern, (e.g. operator, target allocator, github action) -component: operator - -# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). -note: Added reconciliation errors for webhook events. The webhooks run the manifest generators to check for any errors. - -# One or more tracking issues related to the change -issues: [2399] - -# (Optional) One or more lines of additional information to render under the primary note. -# These lines will be padded with 2 spaces and then inserted directly into the document. -# Use pipe (|) for multiline entries. -subtext: \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 02547f0fbc..c9d919240a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,29 @@ +## 0.109.0 + +### 🚩 Deprecations 🚩 + +- `operator`: Deprecated `label` flag and introduced `labels-filter` flag to align the label filtering with the attribute filtering flag name. The `label` flag will be removed when #3236 issue is resolved. (#3218) + +### 💡 Enhancements 💡 + +- `collector`: adds test for memory utilization (#3283) +- `operator`: Added reconciliation errors for webhook events. The webhooks run the manifest generators to check for any errors. (#2399) + +### Components + +* [OpenTelemetry Collector - v0.109.0](https://github.com/open-telemetry/opentelemetry-collector/releases/tag/v0.109.0) +* [OpenTelemetry Contrib - v0.109.0](https://github.com/open-telemetry/opentelemetry-collector-contrib/releases/tag/v0.109.0) +* [Java auto-instrumentation - v1.33.5](https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/tag/v1.33.5) +* [.NET auto-instrumentation - v1.2.0](https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/releases/tag/v1.2.0) +* [Node.JS - v0.52.1](https://github.com/open-telemetry/opentelemetry-js/releases/tag/experimental%2Fv0.52.1) +* [Python - v0.48b0](https://github.com/open-telemetry/opentelemetry-python-contrib/releases/tag/v0.48b0) +* [Go - v0.14.0-alpha](https://github.com/open-telemetry/opentelemetry-go-instrumentation/releases/tag/v0.14.0-alpha) +* [ApacheHTTPD - 1.0.4](https://github.com/open-telemetry/opentelemetry-cpp-contrib/releases/tag/webserver%2Fv1.0.4) +* [Nginx - 1.0.4](https://github.com/open-telemetry/opentelemetry-cpp-contrib/releases/tag/webserver%2Fv1.0.4) + ## 0.108.0 ### 💡 Enhancements 💡 diff --git a/README.md b/README.md index ba115fbe5d..5fe353449e 100644 --- a/README.md +++ b/README.md @@ -759,6 +759,7 @@ The OpenTelemetry Operator _might_ work on versions outside of the given range, | OpenTelemetry Operator | Kubernetes | Cert-Manager | Prometheus-Operator | |------------------------|----------------| ------------ |---------------------| +| v0.109.0 | v1.23 to v1.31 | v1 | v0.76.0 | | v0.108.0 | v1.23 to v1.31 | v1 | v0.76.0 | | v0.107.0 | v1.23 to v1.30 | v1 | v0.75.0 | | v0.106.0 | v1.23 to v1.30 | v1 | v0.75.0 | diff --git a/RELEASE.md b/RELEASE.md index 4463902ce1..1fecc1e997 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -44,9 +44,9 @@ The operator should be released within a week after the [OpenTelemetry collector | Version | Release manager | |----------|-----------------| -| v0.109.0 | @jaronoff97 | | v0.110.0 | @swiatekm | | v0.111.0 | @frzifus | | v0.112.0 | @yuriolisa | | v0.113.0 | @pavolloffay | | v0.114.0 | @TylerHelmuth | +| v0.115.0 | @jaronoff97 | \ No newline at end of file diff --git a/bundle/community/manifests/opentelemetry-operator.clusterserviceversion.yaml b/bundle/community/manifests/opentelemetry-operator.clusterserviceversion.yaml index 6737e75c56..3b1454f8d6 100644 --- a/bundle/community/manifests/opentelemetry-operator.clusterserviceversion.yaml +++ b/bundle/community/manifests/opentelemetry-operator.clusterserviceversion.yaml @@ -99,13 +99,13 @@ metadata: categories: Logging & Tracing,Monitoring certified: "false" containerImage: ghcr.io/open-telemetry/opentelemetry-operator/opentelemetry-operator - createdAt: "2024-09-05T15:16:50Z" + createdAt: "2024-09-19T17:15:52Z" description: Provides the OpenTelemetry components, including the Collector operators.operatorframework.io/builder: operator-sdk-v1.29.0 operators.operatorframework.io/project_layout: go.kubebuilder.io/v3 repository: github.com/open-telemetry/opentelemetry-operator support: OpenTelemetry Community - name: opentelemetry-operator.v0.108.0 + name: opentelemetry-operator.v0.109.0 namespace: placeholder spec: apiservicedefinitions: {} @@ -479,7 +479,7 @@ spec: valueFrom: fieldRef: fieldPath: spec.serviceAccountName - image: ghcr.io/open-telemetry/opentelemetry-operator/opentelemetry-operator:0.108.0 + image: ghcr.io/open-telemetry/opentelemetry-operator/opentelemetry-operator:0.109.0 livenessProbe: httpGet: path: /healthz @@ -587,7 +587,7 @@ spec: minKubeVersion: 1.23.0 provider: name: OpenTelemetry Community - version: 0.108.0 + version: 0.109.0 webhookdefinitions: - admissionReviewVersions: - v1alpha1 diff --git a/bundle/openshift/manifests/opentelemetry-operator.clusterserviceversion.yaml b/bundle/openshift/manifests/opentelemetry-operator.clusterserviceversion.yaml index 89e840e466..70db688513 100644 --- a/bundle/openshift/manifests/opentelemetry-operator.clusterserviceversion.yaml +++ b/bundle/openshift/manifests/opentelemetry-operator.clusterserviceversion.yaml @@ -99,13 +99,13 @@ metadata: categories: Logging & Tracing,Monitoring certified: "false" containerImage: ghcr.io/open-telemetry/opentelemetry-operator/opentelemetry-operator - createdAt: "2024-09-05T15:16:58Z" + createdAt: "2024-09-19T17:16:12Z" description: Provides the OpenTelemetry components, including the Collector operators.operatorframework.io/builder: operator-sdk-v1.29.0 operators.operatorframework.io/project_layout: go.kubebuilder.io/v3 repository: github.com/open-telemetry/opentelemetry-operator support: OpenTelemetry Community - name: opentelemetry-operator.v0.108.0 + name: opentelemetry-operator.v0.109.0 namespace: placeholder spec: apiservicedefinitions: {} @@ -483,7 +483,7 @@ spec: valueFrom: fieldRef: fieldPath: spec.serviceAccountName - image: ghcr.io/open-telemetry/opentelemetry-operator/opentelemetry-operator:0.108.0 + image: ghcr.io/open-telemetry/opentelemetry-operator/opentelemetry-operator:0.109.0 livenessProbe: httpGet: path: /healthz @@ -591,7 +591,7 @@ spec: minKubeVersion: 1.23.0 provider: name: OpenTelemetry Community - version: 0.108.0 + version: 0.109.0 webhookdefinitions: - admissionReviewVersions: - v1alpha1 diff --git a/versions.txt b/versions.txt index 9842d16e59..5ae4d22241 100644 --- a/versions.txt +++ b/versions.txt @@ -2,16 +2,16 @@ # by default with the OpenTelemetry Operator. This would usually be the latest # stable OpenTelemetry version. When you update this file, make sure to update the # the docs as well. -opentelemetry-collector=0.108.0 +opentelemetry-collector=0.109.0 # Represents the current release of the OpenTelemetry Operator. -operator=0.108.0 +operator=0.109.0 # Represents the current release of the Target Allocator. -targetallocator=0.108.0 +targetallocator=0.109.0 # Represents the current release of the Operator OpAMP Bridge. -operator-opamp-bridge=0.108.0 +operator-opamp-bridge=0.109.0 # Represents the current release of Java instrumentation. # Should match autoinstrumentation/java/version.txt From aa5a56f0ec7f32a2ec5ceb4d4174a9d6d5dd6c6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20=C5=9Awi=C4=85tek?= Date: Sun, 22 Sep 2024 20:58:09 +0200 Subject: [PATCH 2/4] Add controller for TargetAllocator CR (#3275) --- apis/v1alpha1/targetallocator_types.go | 3 +- controllers/common.go | 2 +- controllers/suite_test.go | 4 + controllers/targetallocator_controller.go | 152 ++++++++++ .../targetallocator_controller_test.go | 261 ++++++++++++++++++ .../manifests/targetallocator/configmap.go | 39 ++- .../targetallocator/configmap_test.go | 37 ++- .../targetallocator/deployment_test.go | 6 +- .../targetallocator/serviceaccount_test.go | 10 +- .../targetallocator/targetallocator.go | 2 +- internal/status/targetallocator/handle.go | 58 ++++ .../status/targetallocator/targetallocator.go | 31 +++ main.go | 17 ++ 13 files changed, 589 insertions(+), 33 deletions(-) create mode 100644 controllers/targetallocator_controller.go create mode 100644 controllers/targetallocator_controller_test.go create mode 100644 internal/status/targetallocator/handle.go create mode 100644 internal/status/targetallocator/targetallocator.go diff --git a/apis/v1alpha1/targetallocator_types.go b/apis/v1alpha1/targetallocator_types.go index a5e47d924f..15283e935c 100644 --- a/apis/v1alpha1/targetallocator_types.go +++ b/apis/v1alpha1/targetallocator_types.go @@ -21,10 +21,11 @@ import ( ) func init() { - v1beta1.SchemeBuilder.Register(&TargetAllocator{}, &TargetAllocatorList{}) + SchemeBuilder.Register(&TargetAllocator{}, &TargetAllocatorList{}) } //+kubebuilder:object:root=true +//+kubebuilder:storageversion //+kubebuilder:subresource:status // TargetAllocator is the Schema for the targetallocators API. diff --git a/controllers/common.go b/controllers/common.go index 31a84bfd39..3003907913 100644 --- a/controllers/common.go +++ b/controllers/common.go @@ -67,7 +67,7 @@ func BuildCollector(params manifests.Params) ([]client.Object, error) { Recorder: params.Recorder, Log: params.Log, Config: params.Config, - Collector: params.OtelCol, + Collector: ¶ms.OtelCol, TargetAllocator: *params.TargetAllocator, } taResources, err := BuildTargetAllocator(taParams) diff --git a/controllers/suite_test.go b/controllers/suite_test.go index 3e584f8f8a..55a3cf3446 100644 --- a/controllers/suite_test.go +++ b/controllers/suite_test.go @@ -182,6 +182,10 @@ func TestMain(m *testing.M) { fmt.Printf("failed to SetupWebhookWithManager: %v", err) os.Exit(1) } + if err = v1alpha1.SetupTargetAllocatorWebhook(mgr, config.New(), reviewer); err != nil { + fmt.Printf("failed to SetupWebhookWithManager: %v", err) + os.Exit(1) + } if err = v1alpha1.SetupOpAMPBridgeWebhook(mgr, config.New()); err != nil { fmt.Printf("failed to SetupWebhookWithManager: %v", err) diff --git a/controllers/targetallocator_controller.go b/controllers/targetallocator_controller.go new file mode 100644 index 0000000000..6b748e4535 --- /dev/null +++ b/controllers/targetallocator_controller.go @@ -0,0 +1,152 @@ +// Copyright The OpenTelemetry Authors +// +// 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. + +// Package controllers contains the main controller, where the reconciliation starts. +package controllers + +import ( + "context" + + "github.com/go-logr/logr" + monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + policyV1 "k8s.io/api/policy/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/tools/record" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" + "github.com/open-telemetry/opentelemetry-operator/apis/v1beta1" + "github.com/open-telemetry/opentelemetry-operator/internal/config" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/targetallocator" + taStatus "github.com/open-telemetry/opentelemetry-operator/internal/status/targetallocator" + "github.com/open-telemetry/opentelemetry-operator/pkg/featuregate" +) + +// TargetAllocatorReconciler reconciles a TargetAllocator object. +type TargetAllocatorReconciler struct { + client.Client + recorder record.EventRecorder + scheme *runtime.Scheme + log logr.Logger + config config.Config +} + +// TargetAllocatorReconcilerParams is the set of options to build a new TargetAllocatorReconciler. +type TargetAllocatorReconcilerParams struct { + client.Client + Recorder record.EventRecorder + Scheme *runtime.Scheme + Log logr.Logger + Config config.Config +} + +func (r *TargetAllocatorReconciler) getParams(instance v1alpha1.TargetAllocator) targetallocator.Params { + p := targetallocator.Params{ + Config: r.config, + Client: r.Client, + Log: r.log, + Scheme: r.scheme, + Recorder: r.recorder, + TargetAllocator: instance, + } + + return p +} + +// NewTargetAllocatorReconciler creates a new reconciler for TargetAllocator objects. +func NewTargetAllocatorReconciler( + client client.Client, + scheme *runtime.Scheme, + recorder record.EventRecorder, + config config.Config, + logger logr.Logger, +) *TargetAllocatorReconciler { + return &TargetAllocatorReconciler{ + Client: client, + log: logger, + scheme: scheme, + config: config, + recorder: recorder, + } +} + +// TODO: Uncomment the lines below after enabling the TA controller in main.go +// // +kubebuilder:rbac:groups="",resources=pods;configmaps;services;serviceaccounts;persistentvolumeclaims;persistentvolumes,verbs=get;list;watch;create;update;patch;delete +// // +kubebuilder:rbac:groups="",resources=events,verbs=create;patch +// // +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete +// // +kubebuilder:rbac:groups=policy,resources=poddisruptionbudgets,verbs=get;list;watch;create;update;patch;delete +// // +kubebuilder:rbac:groups=monitoring.coreos.com,resources=servicemonitors;podmonitors,verbs=get;list;watch;create;update;patch;delete +// // +kubebuilder:rbac:groups=opentelemetry.io,resources=opentelemetrycollectors,verbs=get;list;watch;update;patch +// // +kubebuilder:rbac:groups=opentelemetry.io,resources=targetallocators,verbs=get;list;watch;update;patch +// // +kubebuilder:rbac:groups=opentelemetry.io,resources=targetallocators/status,verbs=get;update;patch + +// Reconcile the current state of a TargetAllocator resource with the desired state. +func (r *TargetAllocatorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + log := r.log.WithValues("targetallocator", req.NamespacedName) + + var instance v1alpha1.TargetAllocator + if err := r.Client.Get(ctx, req.NamespacedName, &instance); err != nil { + if !apierrors.IsNotFound(err) { + log.Error(err, "unable to fetch TargetAllocator") + } + + // we'll ignore not-found errors, since they can't be fixed by an immediate + // requeue (we'll need to wait for a new notification), and we can get them + // on deleted requests. + return ctrl.Result{}, client.IgnoreNotFound(err) + } + // We have a deletion, short circuit and let the deletion happen + if deletionTimestamp := instance.GetDeletionTimestamp(); deletionTimestamp != nil { + return ctrl.Result{}, nil + } + + if instance.Spec.ManagementState == v1beta1.ManagementStateUnmanaged { + log.Info("Skipping reconciliation for unmanaged TargetAllocator resource", "name", req.String()) + // Stop requeueing for unmanaged TargetAllocator custom resources + return ctrl.Result{}, nil + } + + params := r.getParams(instance) + desiredObjects, buildErr := BuildTargetAllocator(params) + if buildErr != nil { + return ctrl.Result{}, buildErr + } + + err := reconcileDesiredObjects(ctx, r.Client, log, ¶ms.TargetAllocator, params.Scheme, desiredObjects, nil) + return taStatus.HandleReconcileStatus(ctx, log, params, err) +} + +// SetupWithManager tells the manager what our controller is interested in. +func (r *TargetAllocatorReconciler) SetupWithManager(mgr ctrl.Manager) error { + builder := ctrl.NewControllerManagedBy(mgr). + For(&v1alpha1.TargetAllocator{}). + Owns(&corev1.ConfigMap{}). + Owns(&corev1.ServiceAccount{}). + Owns(&corev1.Service{}). + Owns(&appsv1.Deployment{}). + Owns(&corev1.PersistentVolume{}). + Owns(&corev1.PersistentVolumeClaim{}). + Owns(&policyV1.PodDisruptionBudget{}) + + if featuregate.PrometheusOperatorIsAvailable.IsEnabled() { + builder.Owns(&monitoringv1.ServiceMonitor{}) + builder.Owns(&monitoringv1.PodMonitor{}) + } + + return builder.Complete(r) +} diff --git a/controllers/targetallocator_controller_test.go b/controllers/targetallocator_controller_test.go new file mode 100644 index 0000000000..36d28a3f68 --- /dev/null +++ b/controllers/targetallocator_controller_test.go @@ -0,0 +1,261 @@ +// Copyright The OpenTelemetry Authors +// +// 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. + +package controllers_test + +import ( + "context" + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/tools/record" + "sigs.k8s.io/controller-runtime/pkg/client" + logf "sigs.k8s.io/controller-runtime/pkg/log" + k8sreconcile "sigs.k8s.io/controller-runtime/pkg/reconcile" + + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" + "github.com/open-telemetry/opentelemetry-operator/apis/v1beta1" + "github.com/open-telemetry/opentelemetry-operator/controllers" + "github.com/open-telemetry/opentelemetry-operator/internal/config" +) + +var testLogger = logf.Log.WithName("opamp-bridge-controller-unit-tests") + +func TestNewObjectsOnReconciliation_TargetAllocator(t *testing.T) { + // prepare + cfg := config.New( + config.WithTargetAllocatorImage("default-ta"), + ) + nsn := types.NamespacedName{Name: "my-instance", Namespace: "default"} + reconciler := controllers.NewTargetAllocatorReconciler( + k8sClient, + testScheme, + record.NewFakeRecorder(10), + cfg, + testLogger, + ) + created := &v1alpha1.TargetAllocator{ + ObjectMeta: metav1.ObjectMeta{ + Name: nsn.Name, + Namespace: nsn.Namespace, + }, + Spec: v1alpha1.TargetAllocatorSpec{}, + } + err := k8sClient.Create(context.Background(), created) + require.NoError(t, err) + + // test + req := k8sreconcile.Request{ + NamespacedName: nsn, + } + _, err = reconciler.Reconcile(context.Background(), req) + + // verify + require.NoError(t, err) + + // the base query for the underlying objects + opts := []client.ListOption{ + client.InNamespace(nsn.Namespace), + client.MatchingLabels(map[string]string{ + "app.kubernetes.io/instance": fmt.Sprintf("%s.%s", nsn.Namespace, nsn.Name), + "app.kubernetes.io/managed-by": "opentelemetry-operator", + "app.kubernetes.io/component": "opentelemetry-targetallocator", + }), + } + + // verify that we have at least one object for each of the types we create + // whether we have the right ones is up to the specific tests for each type + { + list := &corev1.ConfigMapList{} + err = k8sClient.List(context.Background(), list, opts...) + assert.NoError(t, err) + assert.NotEmpty(t, list.Items) + } + { + list := &corev1.ServiceAccountList{} + err = k8sClient.List(context.Background(), list, opts...) + assert.NoError(t, err) + assert.NotEmpty(t, list.Items) + } + { + list := &corev1.ServiceList{} + err = k8sClient.List(context.Background(), list, opts...) + assert.NoError(t, err) + assert.NotEmpty(t, list.Items) + } + { + list := &appsv1.DeploymentList{} + err = k8sClient.List(context.Background(), list, opts...) + assert.NoError(t, err) + assert.NotEmpty(t, list.Items) + } + + // cleanup + require.NoError(t, k8sClient.Delete(context.Background(), created)) +} + +func TestSkipWhenInstanceDoesNotExist_TargetAllocator(t *testing.T) { + // prepare + cfg := config.New() + nsn := types.NamespacedName{Name: "non-existing-my-instance", Namespace: "default"} + reconciler := controllers.NewTargetAllocatorReconciler( + k8sClient, + testScheme, + record.NewFakeRecorder(10), + cfg, + testLogger, + ) + + // test + req := k8sreconcile.Request{ + NamespacedName: nsn, + } + _, err := reconciler.Reconcile(context.Background(), req) + require.NoError(t, err) + + // the base query for the underlying objects + opts := []client.ListOption{ + client.InNamespace(nsn.Namespace), + client.MatchingLabels(map[string]string{ + "app.kubernetes.io/instance": fmt.Sprintf("%s.%s", nsn.Namespace, nsn.Name), + "app.kubernetes.io/managed-by": "opentelemetry-operator", + "app.kubernetes.io/component": "opentelemetry-targetallocator", + }), + } + + // verify that no objects have been created + var objList appsv1.DeploymentList + err = k8sClient.List(context.Background(), &objList, opts...) + assert.NoError(t, err) + assert.Empty(t, objList.Items) +} + +func TestUnmanaged_TargetAllocator(t *testing.T) { + // prepare + cfg := config.New( + config.WithTargetAllocatorImage("default-ta"), + ) + nsn := types.NamespacedName{Name: "my-instance-unmanaged", Namespace: "default"} + reconciler := controllers.NewTargetAllocatorReconciler( + k8sClient, + testScheme, + record.NewFakeRecorder(10), + cfg, + testLogger, + ) + unmanaged := &v1alpha1.TargetAllocator{ + ObjectMeta: metav1.ObjectMeta{ + Name: nsn.Name, + Namespace: nsn.Namespace, + }, + Spec: v1alpha1.TargetAllocatorSpec{ + OpenTelemetryCommonFields: v1beta1.OpenTelemetryCommonFields{ + ManagementState: v1beta1.ManagementStateUnmanaged, + }, + }, + } + err := k8sClient.Create(context.Background(), unmanaged) + require.NoError(t, err) + + // test + req := k8sreconcile.Request{ + NamespacedName: nsn, + } + _, err = reconciler.Reconcile(context.Background(), req) + + // verify + require.NoError(t, err) + + // the base query for the underlying objects + opts := []client.ListOption{ + client.InNamespace(nsn.Namespace), + client.MatchingLabels(map[string]string{ + "app.kubernetes.io/instance": fmt.Sprintf("%s.%s", nsn.Namespace, nsn.Name), + "app.kubernetes.io/managed-by": "opentelemetry-operator", + "app.kubernetes.io/component": "opentelemetry-targetallocator", + }), + } + + // verify that no objects have been created + var objList appsv1.DeploymentList + err = k8sClient.List(context.Background(), &objList, opts...) + assert.NoError(t, err) + assert.Empty(t, objList.Items) + + // cleanup + require.NoError(t, k8sClient.Delete(context.Background(), unmanaged)) +} + +func TestBuildError_TargetAllocator(t *testing.T) { + // prepare + cfg := config.New( + config.WithTargetAllocatorImage("default-ta"), + ) + nsn := types.NamespacedName{Name: "my-instance-builderror", Namespace: "default"} + reconciler := controllers.NewTargetAllocatorReconciler( + k8sClient, + testScheme, + record.NewFakeRecorder(10), + cfg, + testLogger, + ) + unmanaged := &v1alpha1.TargetAllocator{ + ObjectMeta: metav1.ObjectMeta{ + Name: nsn.Name, + Namespace: nsn.Namespace, + }, + Spec: v1alpha1.TargetAllocatorSpec{ + OpenTelemetryCommonFields: v1beta1.OpenTelemetryCommonFields{ + PodDisruptionBudget: &v1beta1.PodDisruptionBudgetSpec{}, + }, + AllocationStrategy: v1beta1.TargetAllocatorAllocationStrategyLeastWeighted, + }, + } + err := k8sClient.Create(context.Background(), unmanaged) + require.NoError(t, err) + + // test + req := k8sreconcile.Request{ + NamespacedName: nsn, + } + _, err = reconciler.Reconcile(context.Background(), req) + + // verify + require.Error(t, err) + + // the base query for the underlying objects + opts := []client.ListOption{ + client.InNamespace(nsn.Namespace), + client.MatchingLabels(map[string]string{ + "app.kubernetes.io/instance": fmt.Sprintf("%s.%s", nsn.Namespace, nsn.Name), + "app.kubernetes.io/managed-by": "opentelemetry-operator", + "app.kubernetes.io/component": "opentelemetry-targetallocator", + }), + } + + // verify that no objects have been created + var objList appsv1.DeploymentList + err = k8sClient.List(context.Background(), &objList, opts...) + assert.NoError(t, err) + assert.Empty(t, objList.Items) + + // cleanup + require.NoError(t, k8sClient.Delete(context.Background(), unmanaged)) +} diff --git a/internal/manifests/targetallocator/configmap.go b/internal/manifests/targetallocator/configmap.go index f7e0408974..496c625ae3 100644 --- a/internal/manifests/targetallocator/configmap.go +++ b/internal/manifests/targetallocator/configmap.go @@ -38,25 +38,38 @@ func ConfigMap(params Params) (*corev1.ConfigMap, error) { taSpec := instance.Spec taConfig := make(map[interface{}]interface{}) - - taConfig["collector_selector"] = metav1.LabelSelector{ - MatchLabels: manifestutils.SelectorLabels(params.Collector.ObjectMeta, collector.ComponentOpenTelemetryCollector), - } - // Set config if global or scrape configs set config := map[string]interface{}{} - globalConfig, err := getGlobalConfig(taSpec.GlobalConfig, params.Collector.Spec.Config) - if err != nil { - return nil, err + var ( + globalConfig map[string]any + scrapeConfigs []v1beta1.AnyConfig + collectorSelector *metav1.LabelSelector + err error + ) + if params.Collector != nil { + collectorSelector = &metav1.LabelSelector{ + MatchLabels: manifestutils.SelectorLabels(params.Collector.ObjectMeta, collector.ComponentOpenTelemetryCollector), + } + + globalConfig, err = getGlobalConfig(taSpec.GlobalConfig, params.Collector.Spec.Config) + if err != nil { + return nil, err + } + + scrapeConfigs, err = getScrapeConfigs(taSpec.ScrapeConfigs, params.Collector.Spec.Config) + if err != nil { + return nil, err + } + } else { // if there's no collector, just use what's in the TargetAllocator CR + collectorSelector = nil + globalConfig = taSpec.GlobalConfig.Object + scrapeConfigs = taSpec.ScrapeConfigs } + if len(globalConfig) > 0 { config["global"] = globalConfig } - scrapeConfigs, err := getScrapeConfigs(taSpec.ScrapeConfigs, params.Collector.Spec.Config) - if err != nil { - return nil, err - } if len(scrapeConfigs) > 0 { config["scrape_configs"] = scrapeConfigs } @@ -65,6 +78,8 @@ func ConfigMap(params Params) (*corev1.ConfigMap, error) { taConfig["config"] = config } + taConfig["collector_selector"] = collectorSelector + if len(taSpec.AllocationStrategy) > 0 { taConfig["allocation_strategy"] = taSpec.AllocationStrategy } else { diff --git a/internal/manifests/targetallocator/configmap_test.go b/internal/manifests/targetallocator/configmap_test.go index cfa45feb8c..66553bf783 100644 --- a/internal/manifests/targetallocator/configmap_test.go +++ b/internal/manifests/targetallocator/configmap_test.go @@ -31,6 +31,8 @@ import ( func TestDesiredConfigMap(t *testing.T) { expectedLabels := map[string]string{ + "app.kubernetes.io/name": "my-instance-targetallocator", + "app.kubernetes.io/component": "opentelemetry-targetallocator", "app.kubernetes.io/managed-by": "opentelemetry-operator", "app.kubernetes.io/instance": "default.my-instance", "app.kubernetes.io/part-of": "opentelemetry", @@ -47,9 +49,6 @@ func TestDesiredConfigMap(t *testing.T) { } t.Run("should return expected target allocator config map", func(t *testing.T) { - expectedLabels["app.kubernetes.io/component"] = "opentelemetry-targetallocator" - expectedLabels["app.kubernetes.io/name"] = "my-instance-targetallocator" - expectedData := map[string]string{ targetAllocatorFilename: `allocation_strategy: consistent-hashing collector_selector: @@ -79,10 +78,30 @@ filter_strategy: relabel-config assert.Equal(t, expectedData[targetAllocatorFilename], actual.Data[targetAllocatorFilename]) }) - t.Run("should return target allocator config map without scrape configs", func(t *testing.T) { - expectedLabels["app.kubernetes.io/component"] = "opentelemetry-targetallocator" - expectedLabels["app.kubernetes.io/name"] = "my-instance-targetallocator" + t.Run("should return target allocator config map without collector", func(t *testing.T) { + expectedData := map[string]string{ + targetAllocatorFilename: `allocation_strategy: consistent-hashing +collector_selector: null +filter_strategy: relabel-config +`, + } + targetAllocator = targetAllocatorInstance() + targetAllocator.Spec.ScrapeConfigs = []v1beta1.AnyConfig{} + params.TargetAllocator = targetAllocator + testParams := Params{ + Collector: nil, + TargetAllocator: targetAllocator, + } + actual, err := ConfigMap(testParams) + require.NoError(t, err) + params.Collector = collector + + assert.Equal(t, "my-instance-targetallocator", actual.Name) + assert.Equal(t, expectedLabels, actual.Labels) + assert.Equal(t, expectedData[targetAllocatorFilename], actual.Data[targetAllocatorFilename]) + }) + t.Run("should return target allocator config map without scrape configs", func(t *testing.T) { expectedData := map[string]string{ targetAllocatorFilename: `allocation_strategy: consistent-hashing collector_selector: @@ -118,9 +137,6 @@ filter_strategy: relabel-config }) t.Run("should return expected target allocator config map with label selectors", func(t *testing.T) { - expectedLabels["app.kubernetes.io/component"] = "opentelemetry-targetallocator" - expectedLabels["app.kubernetes.io/name"] = "my-instance-targetallocator" - expectedData := map[string]string{ targetAllocatorFilename: `allocation_strategy: consistent-hashing collector_selector: @@ -184,9 +200,6 @@ prometheus_cr: }) t.Run("should return expected target allocator config map with scrape interval set", func(t *testing.T) { - expectedLabels["app.kubernetes.io/component"] = "opentelemetry-targetallocator" - expectedLabels["app.kubernetes.io/name"] = "my-instance-targetallocator" - expectedData := map[string]string{ targetAllocatorFilename: `allocation_strategy: consistent-hashing collector_selector: diff --git a/internal/manifests/targetallocator/deployment_test.go b/internal/manifests/targetallocator/deployment_test.go index cc0d59d906..58e98a9ee3 100644 --- a/internal/manifests/targetallocator/deployment_test.go +++ b/internal/manifests/targetallocator/deployment_test.go @@ -182,7 +182,7 @@ func TestDeploymentPodAnnotations(t *testing.T) { assert.Subset(t, ds.Spec.Template.Annotations, testPodAnnotationValues) } -func collectorInstance() v1beta1.OpenTelemetryCollector { +func collectorInstance() *v1beta1.OpenTelemetryCollector { configYAML, err := os.ReadFile("testdata/test.yaml") if err != nil { fmt.Printf("Error getting yaml file: %v", err) @@ -192,7 +192,7 @@ func collectorInstance() v1beta1.OpenTelemetryCollector { if err != nil { fmt.Printf("Error unmarshalling YAML: %v", err) } - return v1beta1.OpenTelemetryCollector{ + return &v1beta1.OpenTelemetryCollector{ ObjectMeta: metav1.ObjectMeta{ Name: "my-instance", Namespace: "default", @@ -213,7 +213,7 @@ func collectorInstance() v1beta1.OpenTelemetryCollector { func targetAllocatorInstance() v1alpha1.TargetAllocator { collectorInstance := collectorInstance() collectorInstance.Spec.TargetAllocator.Enabled = true - params := manifests.Params{OtelCol: collectorInstance} + params := manifests.Params{OtelCol: *collectorInstance} targetAllocator, _ := collector.TargetAllocator(params) targetAllocator.Spec.Image = "ghcr.io/open-telemetry/opentelemetry-operator/opentelemetry-targetallocator:0.47.0" return *targetAllocator diff --git a/internal/manifests/targetallocator/serviceaccount_test.go b/internal/manifests/targetallocator/serviceaccount_test.go index 717a173ff4..cf79af6cba 100644 --- a/internal/manifests/targetallocator/serviceaccount_test.go +++ b/internal/manifests/targetallocator/serviceaccount_test.go @@ -65,16 +65,20 @@ func TestServiceAccountDefault(t *testing.T) { params := Params{ TargetAllocator: v1alpha1.TargetAllocator{ ObjectMeta: metav1.ObjectMeta{ - Name: "my-instance", + Name: "my-instance", + Namespace: "default", + Annotations: map[string]string{ + "prometheus.io/scrape": "false", + }, }, }, } expected := &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ Name: "my-instance-targetallocator", - Namespace: params.Collector.Namespace, + Namespace: params.TargetAllocator.Namespace, Labels: manifestutils.Labels(params.TargetAllocator.ObjectMeta, "my-instance-targetallocator", params.TargetAllocator.Spec.Image, ComponentOpenTelemetryTargetAllocator, nil), - Annotations: params.Collector.Annotations, + Annotations: params.TargetAllocator.Annotations, }, } diff --git a/internal/manifests/targetallocator/targetallocator.go b/internal/manifests/targetallocator/targetallocator.go index 19a5ed60d8..e1da206f4f 100644 --- a/internal/manifests/targetallocator/targetallocator.go +++ b/internal/manifests/targetallocator/targetallocator.go @@ -62,7 +62,7 @@ type Params struct { Recorder record.EventRecorder Scheme *runtime.Scheme Log logr.Logger - Collector v1beta1.OpenTelemetryCollector + Collector *v1beta1.OpenTelemetryCollector TargetAllocator v1alpha1.TargetAllocator Config config.Config } diff --git a/internal/status/targetallocator/handle.go b/internal/status/targetallocator/handle.go new file mode 100644 index 0000000000..82750ff006 --- /dev/null +++ b/internal/status/targetallocator/handle.go @@ -0,0 +1,58 @@ +// Copyright The OpenTelemetry Authors +// +// 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. + +package targetallocator + +import ( + "context" + "fmt" + + "github.com/go-logr/logr" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/targetallocator" +) + +const ( + eventTypeNormal = "Normal" + eventTypeWarning = "Warning" + + reasonError = "Error" + reasonStatusFailure = "StatusFailure" + reasonInfo = "Info" +) + +// HandleReconcileStatus handles updating the status of the CRDs managed by the operator. +// TODO: make the status more useful https://github.com/open-telemetry/opentelemetry-operator/issues/1972 +func HandleReconcileStatus(ctx context.Context, log logr.Logger, params targetallocator.Params, err error) (ctrl.Result, error) { + log.V(2).Info("updating opampbridge status") + if err != nil { + params.Recorder.Event(¶ms.TargetAllocator, eventTypeWarning, reasonError, err.Error()) + return ctrl.Result{}, err + } + changed := params.TargetAllocator.DeepCopy() + + statusErr := UpdateTargetAllocatorStatus(ctx, params.Client, changed) + if statusErr != nil { + params.Recorder.Event(changed, eventTypeWarning, reasonStatusFailure, statusErr.Error()) + return ctrl.Result{}, statusErr + } + statusPatch := client.MergeFrom(¶ms.TargetAllocator) + if err := params.Client.Status().Patch(ctx, changed, statusPatch); err != nil { + return ctrl.Result{}, fmt.Errorf("failed to apply status changes to the OpenTelemetry CR: %w", err) + } + params.Recorder.Event(changed, eventTypeNormal, reasonInfo, "applied status changes") + return ctrl.Result{}, nil +} diff --git a/internal/status/targetallocator/targetallocator.go b/internal/status/targetallocator/targetallocator.go new file mode 100644 index 0000000000..8c0df97260 --- /dev/null +++ b/internal/status/targetallocator/targetallocator.go @@ -0,0 +1,31 @@ +// Copyright The OpenTelemetry Authors +// +// 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. + +package targetallocator + +import ( + "context" + + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" + "github.com/open-telemetry/opentelemetry-operator/internal/version" +) + +func UpdateTargetAllocatorStatus(ctx context.Context, cli client.Client, changed *v1alpha1.TargetAllocator) error { + if changed.Status.Version == "" { + changed.Status.Version = version.TargetAllocator() + } + return nil +} diff --git a/main.go b/main.go index 951ba2ee1a..a348db6496 100644 --- a/main.go +++ b/main.go @@ -384,6 +384,18 @@ func main() { os.Exit(1) } + // TODO: Uncomment the line below to enable the Target Allocator controller + //if err = controllers.NewTargetAllocatorReconciler( + // mgr.GetClient(), + // mgr.GetScheme(), + // mgr.GetEventRecorderFor("targetallocator"), + // cfg, + // ctrl.Log.WithName("controllers").WithName("TargetAllocator"), + //).SetupWithManager(mgr); err != nil { + // setupLog.Error(err, "unable to create controller", "controller", "TargetAllocator") + // os.Exit(1) + //} + if err = controllers.NewOpAMPBridgeReconciler(controllers.OpAMPBridgeReconcilerParams{ Client: mgr.GetClient(), Log: ctrl.Log.WithName("controllers").WithName("OpAMPBridge"), @@ -430,6 +442,11 @@ func main() { setupLog.Error(err, "unable to create webhook", "webhook", "OpenTelemetryCollector") os.Exit(1) } + // TODO: Uncomment the line below to enable the Target Allocator webhook + //if err = otelv1alpha1.SetupTargetAllocatorWebhook(mgr, cfg, reviewer); err != nil { + // setupLog.Error(err, "unable to create webhook", "webhook", "TargetAllocator") + // os.Exit(1) + //} if err = otelv1alpha1.SetupInstrumentationWebhook(mgr, cfg); err != nil { setupLog.Error(err, "unable to create webhook", "webhook", "Instrumentation") os.Exit(1) From ace1213d26041bb3db4e2e35cb7a8f90c0176ec1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2024 09:56:12 +0200 Subject: [PATCH 3/4] Bump kyverno/action-install-chainsaw from 0.2.9 to 0.2.10 (#3301) Bumps [kyverno/action-install-chainsaw](https://github.com/kyverno/action-install-chainsaw) from 0.2.9 to 0.2.10. - [Release notes](https://github.com/kyverno/action-install-chainsaw/releases) - [Commits](https://github.com/kyverno/action-install-chainsaw/compare/v0.2.9...v0.2.10) --- updated-dependencies: - dependency-name: kyverno/action-install-chainsaw dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/e2e.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml index 08e6da0b83..b31ffa604a 100644 --- a/.github/workflows/e2e.yaml +++ b/.github/workflows/e2e.yaml @@ -57,7 +57,7 @@ jobs: path: bin key: ${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('Makefile') }}-${{ steps.setup-go.outputs.go-version }} - name: Install chainsaw - uses: kyverno/action-install-chainsaw@v0.2.9 + uses: kyverno/action-install-chainsaw@v0.2.10 - name: Install tools run: make install-tools - name: Prepare e2e tests From 8883006a832e8f70cf77fe60069598337eca6c40 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2024 09:56:47 +0200 Subject: [PATCH 4/4] Bump github.com/prometheus/client_golang from 1.20.3 to 1.20.4 (#3300) Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.20.3 to 1.20.4. - [Release notes](https://github.com/prometheus/client_golang/releases) - [Changelog](https://github.com/prometheus/client_golang/blob/main/CHANGELOG.md) - [Commits](https://github.com/prometheus/client_golang/compare/v1.20.3...v1.20.4) --- updated-dependencies: - dependency-name: github.com/prometheus/client_golang dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index edc1b0e8a0..f41e2b1337 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,7 @@ require ( github.com/prometheus-operator/prometheus-operator v0.76.0 github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.76.2 github.com/prometheus-operator/prometheus-operator/pkg/client v0.76.2 - github.com/prometheus/client_golang v1.20.3 + github.com/prometheus/client_golang v1.20.4 github.com/prometheus/common v0.59.1 github.com/prometheus/prometheus v0.54.1 github.com/shirou/gopsutil v3.21.11+incompatible diff --git a/go.sum b/go.sum index b234dda6f1..5209e450ba 100644 --- a/go.sum +++ b/go.sum @@ -546,8 +546,8 @@ github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3O github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.20.3 h1:oPksm4K8B+Vt35tUhw6GbSNSgVlVSBH0qELP/7u83l4= -github.com/prometheus/client_golang v1.20.3/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_golang v1.20.4 h1:Tgh3Yr67PaOv/uTqloMsCEdeuFTatm5zIq5+qNN23vI= +github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=