diff --git a/.chloggen/2833-fix-detector-resourcedetectionprocessor.yaml b/.chloggen/2833-fix-detector-resourcedetectionprocessor.yaml deleted file mode 100755 index effa51536f..0000000000 --- a/.chloggen/2833-fix-detector-resourcedetectionprocessor.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' -change_type: bug_fix - -# 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: "Use the k8snode detector instead of kubernetes for the automatic RBAC creation for the resourcedetector" - -# One or more tracking issues related to the change -issues: [2833] - -# (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/2862-fix-clusterrolebinding-names.yaml b/.chloggen/2862-fix-clusterrolebinding-names.yaml deleted file mode 100755 index 44307f7670..0000000000 --- a/.chloggen/2862-fix-clusterrolebinding-names.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' -change_type: bug_fix - -# 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: "When two Collectors are created with the same name but different namespaces, the ClusterRoleBinding created by the first will be overriden by the second one." - -# One or more tracking issues related to the change -issues: [2862] - -# (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/add-collector-pool-healthy.yaml b/.chloggen/add-collector-pool-healthy.yaml deleted file mode 100755 index 1bc8ca48ed..0000000000 --- a/.chloggen/add-collector-pool-healthy.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: opamp - -# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). -note: Add healthy field at collector pool level in opamp bridge heartbeat - -# One or more tracking issues related to the change -issues: [2936] - -# (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/cleanup-roles.yaml b/.chloggen/cleanup-roles.yaml deleted file mode 100755 index 51be6f2675..0000000000 --- a/.chloggen/cleanup-roles.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' -change_type: bug_fix - -# 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: Cleanup ClusterRoles and ClusterRoleBindings created by the operator - -# One or more tracking issues related to the change -issues: [2938] - -# (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: The operator uses finalizer on the collector to run the cleanup diff --git a/.chloggen/collector-readiness-support.yaml b/.chloggen/collector-readiness-support.yaml deleted file mode 100644 index e27a8d00d6..0000000000 --- a/.chloggen/collector-readiness-support.yaml +++ /dev/null @@ -1,17 +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: Add support for readinessProbe on OpenTelemetryCollector CRD. - -# One or more tracking issues related to the change -issues: [2943] - -# (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: | - Add support for readinessProbe on `OpenTelemetryCollector` and its default similar to the already supported livenessProbe. diff --git a/.chloggen/customized-log-encoder.yaml b/.chloggen/customized-log-encoder.yaml deleted file mode 100755 index b51a8bc593..0000000000 --- a/.chloggen/customized-log-encoder.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: operator - -# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). -note: Enabling new Logs Enconder Configuration parameters. - -# One or more tracking issues related to the change -issues: [268] - -# (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/fix-collector-node-selector.yaml b/.chloggen/fix-collector-node-selector.yaml deleted file mode 100644 index 3674ea046c..0000000000 --- a/.chloggen/fix-collector-node-selector.yaml +++ /dev/null @@ -1,17 +0,0 @@ -# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' -change_type: bug_fix - -# 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: Fix to reflect changes of OpenTelemetryCollector.spec.nodeSelector in the collector Pods - -# One or more tracking issues related to the change -issues: [2940] - -# (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: | - When updating `OpenTelemetryCollector.spec.nodeSelector` it was not removing previous selector from the final collector pod (Deployment/Daemonset/Statefulset). diff --git a/.chloggen/fix-labels-annotations-filter.yaml b/.chloggen/fix-labels-annotations-filter.yaml deleted file mode 100755 index bde0808c84..0000000000 --- a/.chloggen/fix-labels-annotations-filter.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' -change_type: 'bug_fix' - -# 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: Fix of Labels and Annotations filter - -# One or more tracking issues related to the change -issues: [2770] - -# (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/fix-ta-check-policy.yaml b/.chloggen/fix-ta-check-policy.yaml deleted file mode 100755 index bb35c6d77a..0000000000 --- a/.chloggen/fix-ta-check-policy.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' -change_type: 'bug_fix' - -# 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: target-allocator - -# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). -note: Fixed non-expected warnings on TA webhook. - -# One or more tracking issues related to the change -issues: [2685] - -# (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/fix_load-initial-servicemonitors.yaml b/.chloggen/refactor-strategies.yaml similarity index 78% rename from .chloggen/fix_load-initial-servicemonitors.yaml rename to .chloggen/refactor-strategies.yaml index 7466a937e2..5388aafdad 100755 --- a/.chloggen/fix_load-initial-servicemonitors.yaml +++ b/.chloggen/refactor-strategies.yaml @@ -1,16 +1,16 @@ # One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' -change_type: 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: target allocator # A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). -note: Fix target allocator readiness check +note: Refactor allocation strategies # One or more tracking issues related to the change -issues: [2903] +issues: [2928] # (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: +subtext: The performance of the per-node strategy was massively improved as part of this change. diff --git a/.chloggen/replace-create-rbac-permissions-by-checking-the-sa-permissions.yaml b/.chloggen/replace-create-rbac-permissions-by-checking-the-sa-permissions.yaml deleted file mode 100755 index ab5895bb16..0000000000 --- a/.chloggen/replace-create-rbac-permissions-by-checking-the-sa-permissions.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: operator - -# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). -note: Automatically enable RBAC creation if operator SA can create clusterroles and bindings. --create-rbac-permissions flag is noop and deprecated now. - -# One or more tracking issues related to the change -issues: [2588] - -# (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/.chloggen/ta-add-https.yaml b/.chloggen/ta-add-https.yaml deleted file mode 100755 index 1c231c3c18..0000000000 --- a/.chloggen/ta-add-https.yaml +++ /dev/null @@ -1,18 +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: target allocator - -# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). -note: Added option for creating an mTLS-configured HTTPS server to fetch scrape config with real secret values. - -# One or more tracking issues related to the change -issues: [1669] - -# (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: | - The change introduces an option to create an additional HTTPS server with mTLS configuration. - This server is specifically utilized for obtaining the scrape configuration with actual secret values. diff --git a/.chloggen/verify-prom-crd-resources.yaml b/.chloggen/verify-prom-crd-resources.yaml deleted file mode 100755 index 1adfe693f9..0000000000 --- a/.chloggen/verify-prom-crd-resources.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' -change_type: bug_fix - -# 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: Ensure all Prometheus CRDs are installed - -# One or more tracking issues related to the change -issues: [2964] - -# (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/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml index ea6a3d7ead..c87c7824b0 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.0 + uses: kyverno/action-install-chainsaw@v0.2.1 - name: Install tools run: make install-tools - name: Prepare e2e tests diff --git a/CHANGELOG.md b/CHANGELOG.md index a874f6d2ec..66a9ffc1b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,52 @@ +## 0.100.0 + +### 💡 Enhancements 💡 + +- `opamp`: Add healthy field at collector pool level in opamp bridge heartbeat (#2936) +- `collector`: Add support for readinessProbe on OpenTelemetryCollector CRD. (#2943) + Add support for readinessProbe on `OpenTelemetryCollector` and its default similar to the already supported livenessProbe. + +- `operator`: Enabling new Logs Enconder Configuration parameters. (#268) +- `operator`: Automatically enable RBAC creation if operator SA can create clusterroles and bindings. --create-rbac-permissions flag is noop and deprecated now. (#2588) +- `target allocator`: Added option for creating an mTLS-configured HTTPS server to fetch scrape config with real secret values. (#1669) + The change introduces an option to create an additional HTTPS server with mTLS configuration. + This server is specifically utilized for obtaining the scrape configuration with actual secret values. + + +### 🧰 Bug fixes 🧰 + +- `collector`: Create a Service Monitor for the monitoring service and another one for the collector service when the Prometheus exporter is used. (#2877) + Create a Service Monitor for the collector Service when Prometheus exporter is used. A different Service Monitor is created for the monitoring service. + This helps excluding the headless service (duplicating the metrics collection) and splits responsibilities between the two Service Monitors. + Now, the operator.opentelemetry.io/collector-service-type label is used to differentiate the services. + operator.opentelemetry.io/collector-monitoring-service and operator.opentelemetry.io/collector-headless-service are deprecated now. +- `target-allocator`: Fixed non-expected warnings on TA webhook. (#2685) +- `collector`: Ensure all Prometheus CRDs are installed (#2964) +- `collector`: Cleanup ClusterRoles and ClusterRoleBindings created by the operator (#2938) + The operator uses finalizer on the collector to run the cleanup +- `collector`: Use the k8snode detector instead of kubernetes for the automatic RBAC creation for the resourcedetector (#2833) +- `collector`: When two Collectors are created with the same name but different namespaces, the ClusterRoleBinding created by the first will be overriden by the second one. (#2862) +- `collector`: Fix to reflect changes of OpenTelemetryCollector.spec.nodeSelector in the collector Pods (#2940) + When updating `OpenTelemetryCollector.spec.nodeSelector` it was not removing previous selector from the final collector pod (Deployment/Daemonset/Statefulset). + +- `collector`: Fix of Labels and Annotations filter (#2770) +- `target allocator`: Fix target allocator readiness check (#2903) + +### Components + +* [OpenTelemetry Collector - v0.100.0](https://github.com/open-telemetry/opentelemetry-collector/releases/tag/v0.100.0) +* [OpenTelemetry Contrib - v0.100.0](https://github.com/open-telemetry/opentelemetry-collector-contrib/releases/tag/v0.100.0) +* [Java auto-instrumentation - v1.32.1](https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/tag/v1.32.1) +* [.NET auto-instrumentation - v1.2.0](https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/releases/tag/{AUTO_INSTRUMENTATION_DOTNET_VERSION}) +* [Node.JS - v0.51.0](https://github.com/open-telemetry/opentelemetry-js/releases/tag/experimental%2Fv0.51.0) +* [Python - v0.44b0](https://github.com/open-telemetry/opentelemetry-python-contrib/releases/tag/v0.44b0) +* [Go - v0.12.0-alpha](https://github.com/open-telemetry/opentelemetry-go-instrumentation/releases/tag/v0.12.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.99.0 ### 🛑 Breaking changes 🛑 diff --git a/README.md b/README.md index 8b175b9c08..48e4cc7ffb 100644 --- a/README.md +++ b/README.md @@ -730,6 +730,7 @@ The OpenTelemetry Operator _might_ work on versions outside of the given range, | OpenTelemetry Operator | Kubernetes | Cert-Manager | |------------------------| -------------- | ------------ | +| v0.100.0 | v1.23 to v1.29 | v1 | | v0.99.0 | v1.23 to v1.29 | v1 | | v0.98.0 | v1.23 to v1.29 | v1 | | v0.97.0 | v1.23 to v1.29 | v1 | @@ -752,7 +753,6 @@ The OpenTelemetry Operator _might_ work on versions outside of the given range, | v0.80.0 | v1.19 to v1.27 | v1 | | v0.79.0 | v1.19 to v1.27 | v1 | | v0.78.0 | v1.19 to v1.27 | v1 | -| v0.77.0 | v1.19 to v1.26 | v1 | ## Contributing and Developing diff --git a/RELEASE.md b/RELEASE.md index 94cfcbd777..99ea090f88 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -2,7 +2,7 @@ Steps to release a new version of the OpenTelemetry Operator: -1. Create a `Prepare relese x.y.z` pull request with the following content: +1. Create a `Prepare release x.y.z` pull request with the following content: 1. Set the version you're releasing as an environment variable for convenience: `export VERSION=0.n+1.0` 1. Update `versions.txt` - Operator, target allocator and opamp-bridge should be `$VERSION`. @@ -44,9 +44,9 @@ The operator should be released within a week after the [OpenTelemetry collector | Version | Release manager | |----------|-----------------| -| v0.100.0 | @TylerHelmuth | | v0.101.0 | @swiatekm-sumo | | v0.102.0 | @frzifus | | v0.103.0 | @jaronoff97 | | v0.104.0 | @pavolloffay | | v0.105.0 | @yuriolisa | +| v0.106.0 | @TylerHelmuth | \ No newline at end of file diff --git a/bundle/manifests/opentelemetry-operator.clusterserviceversion.yaml b/bundle/manifests/opentelemetry-operator.clusterserviceversion.yaml index c14ddf4e76..fcfd593ee7 100644 --- a/bundle/manifests/opentelemetry-operator.clusterserviceversion.yaml +++ b/bundle/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-05-03T15:21:44Z" + createdAt: "2024-05-15T17:31:49Z" 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.99.0 + name: opentelemetry-operator.v0.100.0 namespace: placeholder spec: apiservicedefinitions: {} @@ -504,7 +504,7 @@ spec: valueFrom: fieldRef: fieldPath: spec.serviceAccountName - image: ghcr.io/open-telemetry/opentelemetry-operator/opentelemetry-operator:0.99.0 + image: ghcr.io/open-telemetry/opentelemetry-operator/opentelemetry-operator:0.100.0 livenessProbe: httpGet: path: /healthz @@ -612,7 +612,7 @@ spec: minKubeVersion: 1.23.0 provider: name: OpenTelemetry Community - version: 0.99.0 + version: 0.100.0 webhookdefinitions: - admissionReviewVersions: - v1alpha1 diff --git a/cmd/otel-allocator/allocation/allocator.go b/cmd/otel-allocator/allocation/allocator.go new file mode 100644 index 0000000000..cbe5d1d31d --- /dev/null +++ b/cmd/otel-allocator/allocation/allocator.go @@ -0,0 +1,310 @@ +// 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 allocation + +import ( + "errors" + "sync" + + "github.com/go-logr/logr" + "github.com/prometheus/client_golang/prometheus" + + "github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator/diff" + "github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator/target" +) + +/* + Target Allocator will serve on an HTTP server exposing /jobs//targets + The targets are allocated using the least connection method + Target Allocator will need information about the collectors in order to set the URLs + Keep a Map of what each collector currently holds and update it based on new scrape target updates +*/ + +var _ Allocator = &allocator{} + +func newAllocator(log logr.Logger, strategy Strategy, opts ...AllocationOption) Allocator { + chAllocator := &allocator{ + strategy: strategy, + collectors: make(map[string]*Collector), + targetItems: make(map[string]*target.Item), + targetItemsPerJobPerCollector: make(map[string]map[string]map[string]bool), + log: log, + } + for _, opt := range opts { + opt(chAllocator) + } + + return chAllocator +} + +type allocator struct { + strategy Strategy + + // collectors is a map from a Collector's name to a Collector instance + // collectorKey -> collector pointer + collectors map[string]*Collector + + // targetItems is a map from a target item's hash to the target items allocated state + // targetItem hash -> target item pointer + targetItems map[string]*target.Item + + // collectorKey -> job -> target item hash -> true + targetItemsPerJobPerCollector map[string]map[string]map[string]bool + + // m protects collectors, targetItems and targetItemsPerJobPerCollector for concurrent use. + m sync.RWMutex + + log logr.Logger + + filter Filter +} + +// SetFilter sets the filtering hook to use. +func (a *allocator) SetFilter(filter Filter) { + a.filter = filter +} + +// SetTargets accepts a list of targets that will be used to make +// load balancing decisions. This method should be called when there are +// new targets discovered or existing targets are shutdown. +func (a *allocator) SetTargets(targets map[string]*target.Item) { + timer := prometheus.NewTimer(TimeToAssign.WithLabelValues("SetTargets", a.strategy.GetName())) + defer timer.ObserveDuration() + + if a.filter != nil { + targets = a.filter.Apply(targets) + } + RecordTargetsKept(targets) + + a.m.Lock() + defer a.m.Unlock() + + // Check for target changes + targetsDiff := diff.Maps(a.targetItems, targets) + // If there are any additions or removals + if len(targetsDiff.Additions()) != 0 || len(targetsDiff.Removals()) != 0 { + a.handleTargets(targetsDiff) + } +} + +// SetCollectors sets the set of collectors with key=collectorName, value=Collector object. +// This method is called when Collectors are added or removed. +func (a *allocator) SetCollectors(collectors map[string]*Collector) { + timer := prometheus.NewTimer(TimeToAssign.WithLabelValues("SetCollectors", a.strategy.GetName())) + defer timer.ObserveDuration() + + CollectorsAllocatable.WithLabelValues(a.strategy.GetName()).Set(float64(len(collectors))) + if len(collectors) == 0 { + a.log.Info("No collector instances present") + } + + a.m.Lock() + defer a.m.Unlock() + + // Check for collector changes + collectorsDiff := diff.Maps(a.collectors, collectors) + if len(collectorsDiff.Additions()) != 0 || len(collectorsDiff.Removals()) != 0 { + a.handleCollectors(collectorsDiff) + } +} + +func (a *allocator) GetTargetsForCollectorAndJob(collector string, job string) []*target.Item { + a.m.RLock() + defer a.m.RUnlock() + if _, ok := a.targetItemsPerJobPerCollector[collector]; !ok { + return []*target.Item{} + } + if _, ok := a.targetItemsPerJobPerCollector[collector][job]; !ok { + return []*target.Item{} + } + targetItemsCopy := make([]*target.Item, len(a.targetItemsPerJobPerCollector[collector][job])) + index := 0 + for targetHash := range a.targetItemsPerJobPerCollector[collector][job] { + targetItemsCopy[index] = a.targetItems[targetHash] + index++ + } + return targetItemsCopy +} + +// TargetItems returns a shallow copy of the targetItems map. +func (a *allocator) TargetItems() map[string]*target.Item { + a.m.RLock() + defer a.m.RUnlock() + targetItemsCopy := make(map[string]*target.Item) + for k, v := range a.targetItems { + targetItemsCopy[k] = v + } + return targetItemsCopy +} + +// Collectors returns a shallow copy of the collectors map. +func (a *allocator) Collectors() map[string]*Collector { + a.m.RLock() + defer a.m.RUnlock() + collectorsCopy := make(map[string]*Collector) + for k, v := range a.collectors { + collectorsCopy[k] = v + } + return collectorsCopy +} + +// handleTargets receives the new and removed targets and reconciles the current state. +// Any removals are removed from the allocator's targetItems and unassigned from the corresponding collector. +// Any net-new additions are assigned to the collector on the same node as the target. +func (a *allocator) handleTargets(diff diff.Changes[*target.Item]) { + // Check for removals + for k, item := range a.targetItems { + // if the current item is in the removals list + if _, ok := diff.Removals()[k]; ok { + a.removeTargetItem(item) + } + } + + // Check for additions + assignmentErrors := []error{} + for k, item := range diff.Additions() { + // Do nothing if the item is already there + if _, ok := a.targetItems[k]; ok { + continue + } else { + // TODO: track target -> collector relationship in a separate map + item.CollectorName = "" + // Add item to item pool and assign a collector + err := a.addTargetToTargetItems(item) + if err != nil { + assignmentErrors = append(assignmentErrors, err) + } + } + } + + // Check for unassigned targets + unassignedTargets := len(assignmentErrors) + if unassignedTargets > 0 { + err := errors.Join(assignmentErrors...) + a.log.Info("Could not assign targets for some jobs", "targets", unassignedTargets, "error", err) + TargetsUnassigned.Set(float64(unassignedTargets)) + } +} + +func (a *allocator) addTargetToTargetItems(tg *target.Item) error { + a.targetItems[tg.Hash()] = tg + if len(a.collectors) == 0 { + return nil + } + + colOwner, err := a.strategy.GetCollectorForTarget(a.collectors, tg) + if err != nil { + return err + } + + // Check if this is a reassignment, if so, unassign first + // note: The ordering here is important, we want to determine the new assignment before unassigning, because + // the strategy might make use of previous assignment information + if _, ok := a.collectors[tg.CollectorName]; ok && tg.CollectorName != "" { + a.unassignTargetItem(tg) + } + + tg.CollectorName = colOwner.Name + a.addCollectorTargetItemMapping(tg) + a.collectors[colOwner.Name].NumTargets++ + TargetsPerCollector.WithLabelValues(colOwner.String(), a.strategy.GetName()).Set(float64(a.collectors[colOwner.String()].NumTargets)) + + return nil +} + +// unassignTargetItem unassigns the target item from its Collector. The target item is still tracked. +func (a *allocator) unassignTargetItem(item *target.Item) { + collectorName := item.CollectorName + if collectorName == "" { + return + } + c, ok := a.collectors[collectorName] + if !ok { + return + } + c.NumTargets-- + TargetsPerCollector.WithLabelValues(item.CollectorName, a.strategy.GetName()).Set(float64(c.NumTargets)) + delete(a.targetItemsPerJobPerCollector[item.CollectorName][item.JobName], item.Hash()) + if len(a.targetItemsPerJobPerCollector[item.CollectorName][item.JobName]) == 0 { + delete(a.targetItemsPerJobPerCollector[item.CollectorName], item.JobName) + } + item.CollectorName = "" +} + +// removeTargetItem removes the target item from its Collector. +func (a *allocator) removeTargetItem(item *target.Item) { + a.unassignTargetItem(item) + delete(a.targetItems, item.Hash()) +} + +// removeCollector removes a Collector from the allocator. +func (a *allocator) removeCollector(collector *Collector) { + delete(a.collectors, collector.Name) + // Remove the collector from any target item records + for _, targetItems := range a.targetItemsPerJobPerCollector[collector.Name] { + for targetHash := range targetItems { + a.targetItems[targetHash].CollectorName = "" + } + } + delete(a.targetItemsPerJobPerCollector, collector.Name) + TargetsPerCollector.WithLabelValues(collector.Name, a.strategy.GetName()).Set(0) +} + +// addCollectorTargetItemMapping keeps track of which collector has which jobs and targets +// this allows the allocator to respond without any extra allocations to http calls. The caller of this method +// has to acquire a lock. +func (a *allocator) addCollectorTargetItemMapping(tg *target.Item) { + if a.targetItemsPerJobPerCollector[tg.CollectorName] == nil { + a.targetItemsPerJobPerCollector[tg.CollectorName] = make(map[string]map[string]bool) + } + if a.targetItemsPerJobPerCollector[tg.CollectorName][tg.JobName] == nil { + a.targetItemsPerJobPerCollector[tg.CollectorName][tg.JobName] = make(map[string]bool) + } + a.targetItemsPerJobPerCollector[tg.CollectorName][tg.JobName][tg.Hash()] = true +} + +// handleCollectors receives the new and removed collectors and reconciles the current state. +// Any removals are removed from the allocator's collectors. New collectors are added to the allocator's collector map. +// Finally, update all targets' collector assignments. +func (a *allocator) handleCollectors(diff diff.Changes[*Collector]) { + // Clear removed collectors + for _, k := range diff.Removals() { + a.removeCollector(k) + } + // Insert the new collectors + for _, i := range diff.Additions() { + a.collectors[i.Name] = NewCollector(i.Name, i.NodeName) + } + + // Set collectors on the strategy + a.strategy.SetCollectors(a.collectors) + + // Re-Allocate all targets + assignmentErrors := []error{} + for _, item := range a.targetItems { + err := a.addTargetToTargetItems(item) + if err != nil { + assignmentErrors = append(assignmentErrors, err) + item.CollectorName = "" + } + } + // Check for unassigned targets + unassignedTargets := len(assignmentErrors) + if unassignedTargets > 0 { + err := errors.Join(assignmentErrors...) + a.log.Info("Could not assign targets for some jobs", "targets", unassignedTargets, "error", err) + TargetsUnassigned.Set(float64(unassignedTargets)) + } +} diff --git a/cmd/otel-allocator/allocation/allocator_test.go b/cmd/otel-allocator/allocation/allocator_test.go new file mode 100644 index 0000000000..55f2bb6dc6 --- /dev/null +++ b/cmd/otel-allocator/allocation/allocator_test.go @@ -0,0 +1,207 @@ +// 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 allocation + +import ( + "testing" + + "github.com/prometheus/common/model" + "github.com/stretchr/testify/assert" + + "github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator/target" +) + +func TestSetCollectors(t *testing.T) { + RunForAllStrategies(t, func(t *testing.T, allocator Allocator) { + cols := MakeNCollectors(3, 0) + allocator.SetCollectors(cols) + + expectedColLen := len(cols) + collectors := allocator.Collectors() + assert.Len(t, collectors, expectedColLen) + + for _, i := range cols { + assert.NotNil(t, collectors[i.Name]) + } + }) +} + +func TestSetTargets(t *testing.T) { + RunForAllStrategies(t, func(t *testing.T, allocator Allocator) { + targets := MakeNNewTargetsWithEmptyCollectors(3, 0) + allocator.SetTargets(targets) + + expectedTargetLen := len(targets) + actualTargets := allocator.TargetItems() + assert.Len(t, actualTargets, expectedTargetLen) + }) +} + +func TestCanSetSingleTarget(t *testing.T) { + RunForAllStrategies(t, func(t *testing.T, allocator Allocator) { + cols := MakeNCollectors(3, 0) + targets := MakeNNewTargetsWithEmptyCollectors(1, 3) + allocator.SetCollectors(cols) + allocator.SetTargets(targets) + actualTargetItems := allocator.TargetItems() + assert.Len(t, actualTargetItems, 1) + for _, item := range actualTargetItems { + assert.NotEmpty(t, item.CollectorName) + } + }) +} + +func TestCanSetTargetsBeforeCollectors(t *testing.T) { + RunForAllStrategies(t, func(t *testing.T, allocator Allocator) { + cols := MakeNCollectors(3, 0) + targets := MakeNNewTargetsWithEmptyCollectors(1, 3) + allocator.SetTargets(targets) + allocator.SetCollectors(cols) + actualTargetItems := allocator.TargetItems() + assert.Len(t, actualTargetItems, 1) + for _, item := range actualTargetItems { + assert.NotEmpty(t, item.CollectorName) + } + }) +} + +func TestAddingAndRemovingTargets(t *testing.T) { + RunForAllStrategies(t, func(t *testing.T, allocator Allocator) { + cols := MakeNCollectors(3, 0) + allocator.SetCollectors(cols) + + initTargets := MakeNNewTargets(6, 3, 0) + + // test that targets and collectors are added properly + allocator.SetTargets(initTargets) + + // verify + expectedTargetLen := len(initTargets) + assert.Len(t, allocator.TargetItems(), expectedTargetLen) + + // prepare second round of targets + tar := MakeNNewTargets(4, 3, 0) + + // test that fewer targets are found - removed + allocator.SetTargets(tar) + + // verify + targetItems := allocator.TargetItems() + expectedNewTargetLen := len(tar) + assert.Len(t, targetItems, expectedNewTargetLen) + + // verify results map + for _, i := range tar { + _, ok := targetItems[i.Hash()] + assert.True(t, ok) + } + }) +} + +func TestAddingAndRemovingCollectors(t *testing.T) { + RunForAllStrategies(t, func(t *testing.T, allocator Allocator) { + targets := MakeNNewTargetsWithEmptyCollectors(3, 0) + allocator.SetTargets(targets) + + collectors := MakeNCollectors(3, 0) + + // test that targets and collectors are added properly + allocator.SetCollectors(collectors) + + // verify + assert.Len(t, allocator.Collectors(), len(collectors)) + for _, tg := range allocator.TargetItems() { + if tg.CollectorName != "" { + assert.Contains(t, collectors, tg.CollectorName) + } + } + + // remove a collector + delete(collectors, "collector-0") + allocator.SetCollectors(collectors) + // verify + assert.Len(t, allocator.Collectors(), len(collectors)) + for _, tg := range allocator.TargetItems() { + if tg.CollectorName != "" { + assert.Contains(t, collectors, tg.CollectorName) + } + } + + // add two more collectors + collectors = MakeNCollectors(5, 0) + allocator.SetCollectors(collectors) + + // verify + assert.Len(t, allocator.Collectors(), len(collectors)) + for _, tg := range allocator.TargetItems() { + if tg.CollectorName != "" { + assert.Contains(t, collectors, tg.CollectorName) + } + } + + // remove all collectors + collectors = map[string]*Collector{} + allocator.SetCollectors(collectors) + + // verify + assert.Len(t, allocator.Collectors(), len(collectors)) + jobs := []string{} + for _, tg := range allocator.TargetItems() { + assert.Empty(t, tg.CollectorName) + jobs = append(jobs, tg.JobName) + } + for _, job := range jobs { + for collector := range collectors { + assert.Empty(t, allocator.GetTargetsForCollectorAndJob(collector, job)) + } + } + }) +} + +// Tests that two targets with the same target url and job name but different label set are both added. +func TestAllocationCollision(t *testing.T) { + RunForAllStrategies(t, func(t *testing.T, allocator Allocator) { + + cols := MakeNCollectors(3, 0) + allocator.SetCollectors(cols) + firstLabels := model.LabelSet{ + "test": "test1", + } + secondLabels := model.LabelSet{ + "test": "test2", + } + firstTarget := target.NewItem("sample-name", "0.0.0.0:8000", firstLabels, "") + secondTarget := target.NewItem("sample-name", "0.0.0.0:8000", secondLabels, "") + + targetList := map[string]*target.Item{ + firstTarget.Hash(): firstTarget, + secondTarget.Hash(): secondTarget, + } + + // test that targets and collectors are added properly + allocator.SetTargets(targetList) + + // verify + targetItems := allocator.TargetItems() + expectedTargetLen := len(targetList) + assert.Len(t, targetItems, expectedTargetLen) + + // verify results map + for _, i := range targetList { + _, ok := targetItems[i.Hash()] + assert.True(t, ok) + } + }) +} diff --git a/cmd/otel-allocator/allocation/consistent_hashing.go b/cmd/otel-allocator/allocation/consistent_hashing.go index f69a2f25d2..8ec07ba857 100644 --- a/cmd/otel-allocator/allocation/consistent_hashing.go +++ b/cmd/otel-allocator/allocation/consistent_hashing.go @@ -15,20 +15,15 @@ package allocation import ( + "fmt" "strings" - "sync" "github.com/buraksezer/consistent" "github.com/cespare/xxhash/v2" - "github.com/go-logr/logr" - "github.com/prometheus/client_golang/prometheus" - "github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator/diff" "github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator/target" ) -var _ Allocator = &consistentHashingAllocator{} - const consistentHashingStrategyName = "consistent-hashing" type hasher struct{} @@ -37,29 +32,14 @@ func (h hasher) Sum64(data []byte) uint64 { return xxhash.Sum64(data) } -type consistentHashingAllocator struct { - // m protects consistentHasher, collectors and targetItems for concurrent use. - m sync.RWMutex +var _ Strategy = &consistentHashingStrategy{} +type consistentHashingStrategy struct { + config consistent.Config consistentHasher *consistent.Consistent - - // collectors is a map from a Collector's name to a Collector instance - // collectorKey -> collector pointer - collectors map[string]*Collector - - // targetItems is a map from a target item's hash to the target items allocated state - // targetItem hash -> target item pointer - targetItems map[string]*target.Item - - // collectorKey -> job -> target item hash -> true - targetItemsPerJobPerCollector map[string]map[string]map[string]bool - - log logr.Logger - - filter Filter } -func newConsistentHashingAllocator(log logr.Logger, opts ...AllocationOption) Allocator { +func newConsistentHashingStrategy() Strategy { config := consistent.Config{ PartitionCount: 1061, ReplicationFactor: 5, @@ -67,228 +47,40 @@ func newConsistentHashingAllocator(log logr.Logger, opts ...AllocationOption) Al Hasher: hasher{}, } consistentHasher := consistent.New(nil, config) - chAllocator := &consistentHashingAllocator{ - consistentHasher: consistentHasher, - collectors: make(map[string]*Collector), - targetItems: make(map[string]*target.Item), - targetItemsPerJobPerCollector: make(map[string]map[string]map[string]bool), - log: log, + chStrategy := &consistentHashingStrategy{ + consistentHasher: consistentHasher, + config: config, } - for _, opt := range opts { - opt(chAllocator) - } - - return chAllocator -} - -// SetFilter sets the filtering hook to use. -func (c *consistentHashingAllocator) SetFilter(filter Filter) { - c.filter = filter + return chStrategy } -// addCollectorTargetItemMapping keeps track of which collector has which jobs and targets -// this allows the allocator to respond without any extra allocations to http calls. The caller of this method -// has to acquire a lock. -func (c *consistentHashingAllocator) addCollectorTargetItemMapping(tg *target.Item) { - if c.targetItemsPerJobPerCollector[tg.CollectorName] == nil { - c.targetItemsPerJobPerCollector[tg.CollectorName] = make(map[string]map[string]bool) - } - if c.targetItemsPerJobPerCollector[tg.CollectorName][tg.JobName] == nil { - c.targetItemsPerJobPerCollector[tg.CollectorName][tg.JobName] = make(map[string]bool) - } - c.targetItemsPerJobPerCollector[tg.CollectorName][tg.JobName][tg.Hash()] = true +func (s *consistentHashingStrategy) GetName() string { + return consistentHashingStrategyName } -// addTargetToTargetItems assigns a target to the collector based on its hash and adds it to the allocator's targetItems -// This method is called from within SetTargets and SetCollectors, which acquire the needed lock. -// This is only called after the collectors are cleared or when a new target has been found in the tempTargetMap. -// INVARIANT: c.collectors must have at least 1 collector set. -// NOTE: by not creating a new target item, there is the potential for a race condition where we modify this target -// item while it's being encoded by the server JSON handler. -func (c *consistentHashingAllocator) addTargetToTargetItems(tg *target.Item) { - // Check if this is a reassignment, if so, decrement the previous collector's NumTargets - if previousColName, ok := c.collectors[tg.CollectorName]; ok { - previousColName.NumTargets-- - delete(c.targetItemsPerJobPerCollector[tg.CollectorName][tg.JobName], tg.Hash()) - TargetsPerCollector.WithLabelValues(previousColName.String(), consistentHashingStrategyName).Set(float64(c.collectors[previousColName.String()].NumTargets)) +func (s *consistentHashingStrategy) GetCollectorForTarget(collectors map[string]*Collector, item *target.Item) (*Collector, error) { + hashKey := strings.Join(item.TargetURL, "") + member := s.consistentHasher.LocateKey([]byte(hashKey)) + collectorName := member.String() + collector, ok := collectors[collectorName] + if !ok { + return nil, fmt.Errorf("unknown collector %s", collectorName) } - colOwner := c.consistentHasher.LocateKey([]byte(strings.Join(tg.TargetURL, ""))) - tg.CollectorName = colOwner.String() - c.targetItems[tg.Hash()] = tg - c.addCollectorTargetItemMapping(tg) - c.collectors[colOwner.String()].NumTargets++ - TargetsPerCollector.WithLabelValues(colOwner.String(), consistentHashingStrategyName).Set(float64(c.collectors[colOwner.String()].NumTargets)) + return collector, nil } -// handleTargets receives the new and removed targets and reconciles the current state. -// Any removals are removed from the allocator's targetItems and unassigned from the corresponding collector. -// Any net-new additions are assigned to the next available collector. -func (c *consistentHashingAllocator) handleTargets(diff diff.Changes[*target.Item]) { - // Check for removals - for k, item := range c.targetItems { - // if the current item is in the removals list - if _, ok := diff.Removals()[k]; ok { - col := c.collectors[item.CollectorName] - col.NumTargets-- - delete(c.targetItems, k) - delete(c.targetItemsPerJobPerCollector[item.CollectorName][item.JobName], item.Hash()) - TargetsPerCollector.WithLabelValues(item.CollectorName, consistentHashingStrategyName).Set(float64(col.NumTargets)) - } - } +func (s *consistentHashingStrategy) SetCollectors(collectors map[string]*Collector) { + // we simply recreate the hasher with the new member set + // this isn't any more expensive than doing a diff and then applying the change + var members []consistent.Member - // Check for additions - for k, item := range diff.Additions() { - // Do nothing if the item is already there - if _, ok := c.targetItems[k]; ok { - continue - } else { - // Add item to item pool and assign a collector - c.addTargetToTargetItems(item) + if len(collectors) > 0 { + members = make([]consistent.Member, 0, len(collectors)) + for _, collector := range collectors { + members = append(members, collector) } } -} - -// handleCollectors receives the new and removed collectors and reconciles the current state. -// Any removals are removed from the allocator's collectors. New collectors are added to the allocator's collector map. -// Finally, update all targets' collectors to match the consistent hashing. -func (c *consistentHashingAllocator) handleCollectors(diff diff.Changes[*Collector]) { - // Clear removed collectors - for _, k := range diff.Removals() { - delete(c.collectors, k.Name) - delete(c.targetItemsPerJobPerCollector, k.Name) - c.consistentHasher.Remove(k.Name) - TargetsPerCollector.WithLabelValues(k.Name, consistentHashingStrategyName).Set(0) - } - // Insert the new collectors - for _, i := range diff.Additions() { - c.collectors[i.Name] = NewCollector(i.Name, i.NodeName) - c.consistentHasher.Add(c.collectors[i.Name]) - } - - // Re-Allocate all targets - for _, item := range c.targetItems { - c.addTargetToTargetItems(item) - } -} - -// SetTargets accepts a list of targets that will be used to make -// load balancing decisions. This method should be called when there are -// new targets discovered or existing targets are shutdown. -func (c *consistentHashingAllocator) SetTargets(targets map[string]*target.Item) { - timer := prometheus.NewTimer(TimeToAssign.WithLabelValues("SetTargets", consistentHashingStrategyName)) - defer timer.ObserveDuration() - - if c.filter != nil { - targets = c.filter.Apply(targets) - } - RecordTargetsKept(targets) - c.m.Lock() - defer c.m.Unlock() + s.consistentHasher = consistent.New(members, s.config) - if len(c.collectors) == 0 { - c.log.Info("No collector instances present, saving targets to allocate to collector(s)") - // If there were no targets discovered previously, assign this as the new set of target items - if len(c.targetItems) == 0 { - c.log.Info("Not discovered any targets previously, saving targets found to the targetItems set") - for k, item := range targets { - c.targetItems[k] = item - } - } else { - // If there were previously discovered targets, add or remove accordingly - targetsDiffEmptyCollectorSet := diff.Maps(c.targetItems, targets) - - // Check for additions - if len(targetsDiffEmptyCollectorSet.Additions()) > 0 { - c.log.Info("New targets discovered, adding new targets to the targetItems set") - for k, item := range targetsDiffEmptyCollectorSet.Additions() { - // Do nothing if the item is already there - if _, ok := c.targetItems[k]; ok { - continue - } else { - // Add item to item pool - c.targetItems[k] = item - } - } - } - - // Check for deletions - if len(targetsDiffEmptyCollectorSet.Removals()) > 0 { - c.log.Info("Targets removed, Removing targets from the targetItems set") - for k := range targetsDiffEmptyCollectorSet.Removals() { - // Delete item from target items - delete(c.targetItems, k) - } - } - } - return - } - // Check for target changes - targetsDiff := diff.Maps(c.targetItems, targets) - // If there are any additions or removals - if len(targetsDiff.Additions()) != 0 || len(targetsDiff.Removals()) != 0 { - c.handleTargets(targetsDiff) - } -} - -// SetCollectors sets the set of collectors with key=collectorName, value=Collector object. -// This method is called when Collectors are added or removed. -func (c *consistentHashingAllocator) SetCollectors(collectors map[string]*Collector) { - timer := prometheus.NewTimer(TimeToAssign.WithLabelValues("SetCollectors", consistentHashingStrategyName)) - defer timer.ObserveDuration() - - CollectorsAllocatable.WithLabelValues(consistentHashingStrategyName).Set(float64(len(collectors))) - if len(collectors) == 0 { - c.log.Info("No collector instances present") - return - } - - c.m.Lock() - defer c.m.Unlock() - - // Check for collector changes - collectorsDiff := diff.Maps(c.collectors, collectors) - if len(collectorsDiff.Additions()) != 0 || len(collectorsDiff.Removals()) != 0 { - c.handleCollectors(collectorsDiff) - } -} - -func (c *consistentHashingAllocator) GetTargetsForCollectorAndJob(collector string, job string) []*target.Item { - c.m.RLock() - defer c.m.RUnlock() - if _, ok := c.targetItemsPerJobPerCollector[collector]; !ok { - return []*target.Item{} - } - if _, ok := c.targetItemsPerJobPerCollector[collector][job]; !ok { - return []*target.Item{} - } - targetItemsCopy := make([]*target.Item, len(c.targetItemsPerJobPerCollector[collector][job])) - index := 0 - for targetHash := range c.targetItemsPerJobPerCollector[collector][job] { - targetItemsCopy[index] = c.targetItems[targetHash] - index++ - } - return targetItemsCopy -} - -// TargetItems returns a shallow copy of the targetItems map. -func (c *consistentHashingAllocator) TargetItems() map[string]*target.Item { - c.m.RLock() - defer c.m.RUnlock() - targetItemsCopy := make(map[string]*target.Item) - for k, v := range c.targetItems { - targetItemsCopy[k] = v - } - return targetItemsCopy -} - -// Collectors returns a shallow copy of the collectors map. -func (c *consistentHashingAllocator) Collectors() map[string]*Collector { - c.m.RLock() - defer c.m.RUnlock() - collectorsCopy := make(map[string]*Collector) - for k, v := range c.collectors { - collectorsCopy[k] = v - } - return collectorsCopy } diff --git a/cmd/otel-allocator/allocation/consistent_hashing_test.go b/cmd/otel-allocator/allocation/consistent_hashing_test.go index bbd4295202..206497ebd2 100644 --- a/cmd/otel-allocator/allocation/consistent_hashing_test.go +++ b/cmd/otel-allocator/allocation/consistent_hashing_test.go @@ -20,25 +20,13 @@ import ( "github.com/stretchr/testify/assert" ) -func TestCanSetSingleTarget(t *testing.T) { - cols := MakeNCollectors(3, 0) - c := newConsistentHashingAllocator(logger) - c.SetCollectors(cols) - c.SetTargets(MakeNNewTargets(1, 3, 0)) - actualTargetItems := c.TargetItems() - assert.Len(t, actualTargetItems, 1) - for _, item := range actualTargetItems { - assert.Equal(t, "collector-0", item.CollectorName) - } -} - func TestRelativelyEvenDistribution(t *testing.T) { numCols := 15 numItems := 10000 cols := MakeNCollectors(numCols, 0) var expectedPerCollector = float64(numItems / numCols) expectedDelta := (expectedPerCollector * 1.5) - expectedPerCollector - c := newConsistentHashingAllocator(logger) + c, _ := New("consistent-hashing", logger) c.SetCollectors(cols) c.SetTargets(MakeNNewTargets(numItems, 0, 0)) actualTargetItems := c.TargetItems() @@ -52,7 +40,7 @@ func TestRelativelyEvenDistribution(t *testing.T) { func TestFullReallocation(t *testing.T) { cols := MakeNCollectors(10, 0) - c := newConsistentHashingAllocator(logger) + c, _ := New("consistent-hashing", logger) c.SetCollectors(cols) c.SetTargets(MakeNNewTargets(10000, 10, 0)) actualTargetItems := c.TargetItems() @@ -77,7 +65,7 @@ func TestNumRemapped(t *testing.T) { numFinalCols := 16 expectedDelta := float64((numFinalCols - numInitialCols) * (numItems / numFinalCols)) cols := MakeNCollectors(numInitialCols, 0) - c := newConsistentHashingAllocator(logger) + c, _ := New("consistent-hashing", logger) c.SetCollectors(cols) c.SetTargets(MakeNNewTargets(numItems, numInitialCols, 0)) actualTargetItems := c.TargetItems() @@ -106,7 +94,7 @@ func TestNumRemapped(t *testing.T) { func TestTargetsWithNoCollectorsConsistentHashing(t *testing.T) { - c := newConsistentHashingAllocator(logger) + c, _ := New("consistent-hashing", logger) // Adding 10 new targets numItems := 10 diff --git a/cmd/otel-allocator/allocation/least_weighted.go b/cmd/otel-allocator/allocation/least_weighted.go index 55a9551efd..caa2febbd9 100644 --- a/cmd/otel-allocator/allocation/least_weighted.go +++ b/cmd/otel-allocator/allocation/least_weighted.go @@ -15,98 +15,34 @@ package allocation import ( - "sync" - - "github.com/go-logr/logr" - "github.com/prometheus/client_golang/prometheus" - - "github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator/diff" "github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator/target" ) -var _ Allocator = &leastWeightedAllocator{} - const leastWeightedStrategyName = "least-weighted" -/* - Target Allocator will serve on an HTTP server exposing /jobs//targets - The targets are allocated using the least connection method - Target Allocator will need information about the collectors in order to set the URLs - Keep a Map of what each collector currently holds and update it based on new scrape target updates -*/ - -// leastWeightedAllocator makes decisions to distribute work among -// a number of OpenTelemetry collectors based on the number of targets. -// Users need to call SetTargets when they have new targets in their -// clusters and call SetCollectors when the collectors have changed. -type leastWeightedAllocator struct { - // m protects collectors and targetItems for concurrent use. - m sync.RWMutex - // collectors is a map from a Collector's name to a Collector instance - collectors map[string]*Collector - // targetItems is a map from a target item's hash to the target items allocated state - targetItems map[string]*target.Item - - // collectorKey -> job -> target item hash -> true - targetItemsPerJobPerCollector map[string]map[string]map[string]bool - - log logr.Logger - - filter Filter -} +var _ Strategy = &leastWeightedStrategy{} -// SetFilter sets the filtering hook to use. -func (allocator *leastWeightedAllocator) SetFilter(filter Filter) { - allocator.filter = filter -} +type leastWeightedStrategy struct{} -func (allocator *leastWeightedAllocator) GetTargetsForCollectorAndJob(collector string, job string) []*target.Item { - allocator.m.RLock() - defer allocator.m.RUnlock() - if _, ok := allocator.targetItemsPerJobPerCollector[collector]; !ok { - return []*target.Item{} - } - if _, ok := allocator.targetItemsPerJobPerCollector[collector][job]; !ok { - return []*target.Item{} - } - targetItemsCopy := make([]*target.Item, len(allocator.targetItemsPerJobPerCollector[collector][job])) - index := 0 - for targetHash := range allocator.targetItemsPerJobPerCollector[collector][job] { - targetItemsCopy[index] = allocator.targetItems[targetHash] - index++ - } - return targetItemsCopy +func newleastWeightedStrategy() Strategy { + return &leastWeightedStrategy{} } -// TargetItems returns a shallow copy of the targetItems map. -func (allocator *leastWeightedAllocator) TargetItems() map[string]*target.Item { - allocator.m.RLock() - defer allocator.m.RUnlock() - targetItemsCopy := make(map[string]*target.Item) - for k, v := range allocator.targetItems { - targetItemsCopy[k] = v - } - return targetItemsCopy +func (s *leastWeightedStrategy) GetName() string { + return leastWeightedStrategyName } -// Collectors returns a shallow copy of the collectors map. -func (allocator *leastWeightedAllocator) Collectors() map[string]*Collector { - allocator.m.RLock() - defer allocator.m.RUnlock() - collectorsCopy := make(map[string]*Collector) - for k, v := range allocator.collectors { - collectorsCopy[k] = v +func (s *leastWeightedStrategy) GetCollectorForTarget(collectors map[string]*Collector, item *target.Item) (*Collector, error) { + // if a collector is already assigned, do nothing + // TODO: track this in a separate map + if item.CollectorName != "" { + if col, ok := collectors[item.CollectorName]; ok { + return col, nil + } } - return collectorsCopy -} -// findNextCollector finds the next collector with fewer number of targets. -// This method is called from within SetTargets and SetCollectors, whose caller -// acquires the needed lock. This method assumes there are is at least 1 collector set. -// INVARIANT: allocator.collectors must have at least 1 collector set. -func (allocator *leastWeightedAllocator) findNextCollector() *Collector { var col *Collector - for _, v := range allocator.collectors { + for _, v := range collectors { // If the initial collector is empty, set the initial collector to the first element of map if col == nil { col = v @@ -114,192 +50,7 @@ func (allocator *leastWeightedAllocator) findNextCollector() *Collector { col = v } } - return col -} - -// addCollectorTargetItemMapping keeps track of which collector has which jobs and targets -// this allows the allocator to respond without any extra allocations to http calls. The caller of this method -// has to acquire a lock. -func (allocator *leastWeightedAllocator) addCollectorTargetItemMapping(tg *target.Item) { - if allocator.targetItemsPerJobPerCollector[tg.CollectorName] == nil { - allocator.targetItemsPerJobPerCollector[tg.CollectorName] = make(map[string]map[string]bool) - } - if allocator.targetItemsPerJobPerCollector[tg.CollectorName][tg.JobName] == nil { - allocator.targetItemsPerJobPerCollector[tg.CollectorName][tg.JobName] = make(map[string]bool) - } - allocator.targetItemsPerJobPerCollector[tg.CollectorName][tg.JobName][tg.Hash()] = true -} - -// addTargetToTargetItems assigns a target to the next available collector and adds it to the allocator's targetItems -// This method is called from within SetTargets and SetCollectors, which acquire the needed lock. -// This is only called after the collectors are cleared or when a new target has been found in the tempTargetMap. -// INVARIANT: allocator.collectors must have at least 1 collector set. -// NOTE: by not creating a new target item, there is the potential for a race condition where we modify this target -// item while it's being encoded by the server JSON handler. -func (allocator *leastWeightedAllocator) addTargetToTargetItems(tg *target.Item) { - chosenCollector := allocator.findNextCollector() - tg.CollectorName = chosenCollector.Name - allocator.targetItems[tg.Hash()] = tg - allocator.addCollectorTargetItemMapping(tg) - chosenCollector.NumTargets++ - TargetsPerCollector.WithLabelValues(chosenCollector.Name, leastWeightedStrategyName).Set(float64(chosenCollector.NumTargets)) -} - -// handleTargets receives the new and removed targets and reconciles the current state. -// Any removals are removed from the allocator's targetItems and unassigned from the corresponding collector. -// Any net-new additions are assigned to the next available collector. -func (allocator *leastWeightedAllocator) handleTargets(diff diff.Changes[*target.Item]) { - // Check for removals - for k, item := range allocator.targetItems { - // if the current item is in the removals list - if _, ok := diff.Removals()[k]; ok { - c := allocator.collectors[item.CollectorName] - c.NumTargets-- - delete(allocator.targetItems, k) - delete(allocator.targetItemsPerJobPerCollector[item.CollectorName][item.JobName], item.Hash()) - TargetsPerCollector.WithLabelValues(item.CollectorName, leastWeightedStrategyName).Set(float64(c.NumTargets)) - } - } - - // Check for additions - for k, item := range diff.Additions() { - // Do nothing if the item is already there - if _, ok := allocator.targetItems[k]; ok { - continue - } else { - // Add item to item pool and assign a collector - allocator.addTargetToTargetItems(item) - } - } -} - -// handleCollectors receives the new and removed collectors and reconciles the current state. -// Any removals are removed from the allocator's collectors. New collectors are added to the allocator's collector map. -// Finally, any targets of removed collectors are reallocated to the next available collector. -func (allocator *leastWeightedAllocator) handleCollectors(diff diff.Changes[*Collector]) { - // Clear removed collectors - for _, k := range diff.Removals() { - delete(allocator.collectors, k.Name) - delete(allocator.targetItemsPerJobPerCollector, k.Name) - TargetsPerCollector.WithLabelValues(k.Name, leastWeightedStrategyName).Set(0) - } - - // If previously there were no collector instances present, allocate the previous set of saved targets to the new collectors - allocateTargets := false - if len(allocator.collectors) == 0 && len(allocator.targetItems) > 0 { - allocateTargets = true - } - // Insert the new collectors - for _, i := range diff.Additions() { - allocator.collectors[i.Name] = NewCollector(i.Name, i.NodeName) - } - if allocateTargets { - for _, item := range allocator.targetItems { - allocator.addTargetToTargetItems(item) - } - } - - // Re-Allocate targets of the removed collectors - for _, item := range allocator.targetItems { - if _, ok := diff.Removals()[item.CollectorName]; ok { - allocator.addTargetToTargetItems(item) - } - } + return col, nil } -// SetTargets accepts a list of targets that will be used to make -// load balancing decisions. This method should be called when there are -// new targets discovered or existing targets are shutdown. -func (allocator *leastWeightedAllocator) SetTargets(targets map[string]*target.Item) { - timer := prometheus.NewTimer(TimeToAssign.WithLabelValues("SetTargets", leastWeightedStrategyName)) - defer timer.ObserveDuration() - - if allocator.filter != nil { - targets = allocator.filter.Apply(targets) - } - RecordTargetsKept(targets) - - allocator.m.Lock() - defer allocator.m.Unlock() - - if len(allocator.collectors) == 0 { - allocator.log.Info("No collector instances present, saving targets to allocate to collector(s)") - // If there were no targets discovered previously, assign this as the new set of target items - if len(allocator.targetItems) == 0 { - allocator.log.Info("Not discovered any targets previously, saving targets found to the targetItems set") - for k, item := range targets { - allocator.targetItems[k] = item - } - } else { - // If there were previously discovered targets, add or remove accordingly - targetsDiffEmptyCollectorSet := diff.Maps(allocator.targetItems, targets) - - // Check for additions - if len(targetsDiffEmptyCollectorSet.Additions()) > 0 { - allocator.log.Info("New targets discovered, adding new targets to the targetItems set") - for k, item := range targetsDiffEmptyCollectorSet.Additions() { - // Do nothing if the item is already there - if _, ok := allocator.targetItems[k]; ok { - continue - } else { - // Add item to item pool - allocator.targetItems[k] = item - } - } - } - - // Check for deletions - if len(targetsDiffEmptyCollectorSet.Removals()) > 0 { - allocator.log.Info("Targets removed, Removing targets from the targetItems set") - for k := range targetsDiffEmptyCollectorSet.Removals() { - // Delete item from target items - delete(allocator.targetItems, k) - } - } - } - return - } - // Check for target changes - targetsDiff := diff.Maps(allocator.targetItems, targets) - // If there are any additions or removals - if len(targetsDiff.Additions()) != 0 || len(targetsDiff.Removals()) != 0 { - allocator.handleTargets(targetsDiff) - } -} - -// SetCollectors sets the set of collectors with key=collectorName, value=Collector object. -// This method is called when Collectors are added or removed. -func (allocator *leastWeightedAllocator) SetCollectors(collectors map[string]*Collector) { - timer := prometheus.NewTimer(TimeToAssign.WithLabelValues("SetCollectors", leastWeightedStrategyName)) - defer timer.ObserveDuration() - - CollectorsAllocatable.WithLabelValues(leastWeightedStrategyName).Set(float64(len(collectors))) - if len(collectors) == 0 { - allocator.log.Info("No collector instances present") - return - } - - allocator.m.Lock() - defer allocator.m.Unlock() - - // Check for collector changes - collectorsDiff := diff.Maps(allocator.collectors, collectors) - if len(collectorsDiff.Additions()) != 0 || len(collectorsDiff.Removals()) != 0 { - allocator.handleCollectors(collectorsDiff) - } -} - -func newLeastWeightedAllocator(log logr.Logger, opts ...AllocationOption) Allocator { - lwAllocator := &leastWeightedAllocator{ - log: log, - collectors: make(map[string]*Collector), - targetItems: make(map[string]*target.Item), - targetItemsPerJobPerCollector: make(map[string]map[string]map[string]bool), - } - - for _, opt := range opts { - opt(lwAllocator) - } - - return lwAllocator -} +func (s *leastWeightedStrategy) SetCollectors(_ map[string]*Collector) {} diff --git a/cmd/otel-allocator/allocation/least_weighted_test.go b/cmd/otel-allocator/allocation/least_weighted_test.go index 0fa91953af..27a149ad33 100644 --- a/cmd/otel-allocator/allocation/least_weighted_test.go +++ b/cmd/otel-allocator/allocation/least_weighted_test.go @@ -20,100 +20,12 @@ import ( "math/rand" "testing" - "github.com/prometheus/common/model" "github.com/stretchr/testify/assert" logf "sigs.k8s.io/controller-runtime/pkg/log" - - "github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator/target" ) var logger = logf.Log.WithName("unit-tests") -func TestSetCollectors(t *testing.T) { - s, _ := New("least-weighted", logger) - - cols := MakeNCollectors(3, 0) - s.SetCollectors(cols) - - expectedColLen := len(cols) - collectors := s.Collectors() - assert.Len(t, collectors, expectedColLen) - - for _, i := range cols { - assert.NotNil(t, collectors[i.Name]) - } -} - -func TestAddingAndRemovingTargets(t *testing.T) { - // prepare allocator with initial targets and collectors - s, _ := New("least-weighted", logger) - - cols := MakeNCollectors(3, 0) - s.SetCollectors(cols) - - initTargets := MakeNNewTargets(6, 3, 0) - - // test that targets and collectors are added properly - s.SetTargets(initTargets) - - // verify - expectedTargetLen := len(initTargets) - assert.Len(t, s.TargetItems(), expectedTargetLen) - - // prepare second round of targets - tar := MakeNNewTargets(4, 3, 0) - - // test that fewer targets are found - removed - s.SetTargets(tar) - - // verify - targetItems := s.TargetItems() - expectedNewTargetLen := len(tar) - assert.Len(t, targetItems, expectedNewTargetLen) - - // verify results map - for _, i := range tar { - _, ok := targetItems[i.Hash()] - assert.True(t, ok) - } -} - -// Tests that two targets with the same target url and job name but different label set are both added. -func TestAllocationCollision(t *testing.T) { - // prepare allocator with initial targets and collectors - s, _ := New("least-weighted", logger) - - cols := MakeNCollectors(3, 0) - s.SetCollectors(cols) - firstLabels := model.LabelSet{ - "test": "test1", - } - secondLabels := model.LabelSet{ - "test": "test2", - } - firstTarget := target.NewItem("sample-name", "0.0.0.0:8000", firstLabels, "") - secondTarget := target.NewItem("sample-name", "0.0.0.0:8000", secondLabels, "") - - targetList := map[string]*target.Item{ - firstTarget.Hash(): firstTarget, - secondTarget.Hash(): secondTarget, - } - - // test that targets and collectors are added properly - s.SetTargets(targetList) - - // verify - targetItems := s.TargetItems() - expectedTargetLen := len(targetList) - assert.Len(t, targetItems, expectedTargetLen) - - // verify results map - for _, i := range targetList { - _, ok := targetItems[i.Hash()] - assert.True(t, ok) - } -} - func TestNoCollectorReassignment(t *testing.T) { s, _ := New("least-weighted", logger) @@ -192,7 +104,7 @@ func TestNoAssignmentToNewCollector(t *testing.T) { // new collector should have no targets newCollector := s.Collectors()[newColName] - assert.Equal(t, newCollector.NumTargets, 0) + assert.Equal(t, 0, newCollector.NumTargets) } // Tests that the delta in number of targets per collector is less than 15% of an even distribution. diff --git a/cmd/otel-allocator/allocation/per_node.go b/cmd/otel-allocator/allocation/per_node.go index dff1b218c3..a5e2bfa3f8 100644 --- a/cmd/otel-allocator/allocation/per_node.go +++ b/cmd/otel-allocator/allocation/per_node.go @@ -15,228 +15,43 @@ package allocation import ( - "sync" + "fmt" - "github.com/go-logr/logr" - "github.com/prometheus/client_golang/prometheus" - - "github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator/diff" "github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator/target" ) -var _ Allocator = &perNodeAllocator{} - const perNodeStrategyName = "per-node" -// perNodeAllocator makes decisions to distribute work among -// a number of OpenTelemetry collectors based on the node on which -// the collector is running. This allocator should be used only when -// collectors are running as daemon set (agent) on each node. -// Users need to call SetTargets when they have new targets in their -// clusters and call SetCollectors when the collectors have changed. -type perNodeAllocator struct { - // m protects collectors and targetItems for concurrent use. - m sync.RWMutex - // collectors is a map from a Collector's node name to a Collector instance - collectors map[string]*Collector - // targetItems is a map from a target item's hash to the target items allocated state - targetItems map[string]*target.Item - - // collectorKey -> job -> target item hash -> true - targetItemsPerJobPerCollector map[string]map[string]map[string]bool - - log logr.Logger - - filter Filter -} - -// SetCollectors sets the set of collectors with key=collectorName, value=Collector object. -// This method is called when Collectors are added or removed. -func (allocator *perNodeAllocator) SetCollectors(collectors map[string]*Collector) { - timer := prometheus.NewTimer(TimeToAssign.WithLabelValues("SetCollectors", perNodeStrategyName)) - defer timer.ObserveDuration() - - CollectorsAllocatable.WithLabelValues(perNodeStrategyName).Set(float64(len(collectors))) - if len(collectors) == 0 { - allocator.log.Info("No collector instances present") - return - } - - allocator.m.Lock() - defer allocator.m.Unlock() - - // Check for collector changes - collectorsDiff := diff.Maps(allocator.collectors, collectors) - if len(collectorsDiff.Additions()) != 0 || len(collectorsDiff.Removals()) != 0 { - for _, k := range allocator.collectors { - delete(allocator.collectors, k.NodeName) - delete(allocator.targetItemsPerJobPerCollector, k.Name) - TargetsPerCollector.WithLabelValues(k.Name, perNodeStrategyName).Set(0) - } - - for _, k := range collectors { - allocator.collectors[k.NodeName] = NewCollector(k.Name, k.NodeName) - } +var _ Strategy = &perNodeStrategy{} - // Re-allocate any already existing targets. - for _, item := range allocator.targetItems { - allocator.addTargetToTargetItems(item) - } - } +type perNodeStrategy struct { + collectorByNode map[string]*Collector } -// SetTargets accepts a list of targets that will be used to make -// load balancing decisions. This method should be called when there are -// new targets discovered or existing targets are shutdown. -func (allocator *perNodeAllocator) SetTargets(targets map[string]*target.Item) { - timer := prometheus.NewTimer(TimeToAssign.WithLabelValues("SetTargets", perNodeStrategyName)) - defer timer.ObserveDuration() - - if allocator.filter != nil { - targets = allocator.filter.Apply(targets) - } - RecordTargetsKept(targets) - - allocator.m.Lock() - defer allocator.m.Unlock() - - // Check for target changes - targetsDiff := diff.Maps(allocator.targetItems, targets) - // If there are any additions or removals - if len(targetsDiff.Additions()) != 0 || len(targetsDiff.Removals()) != 0 { - allocator.handleTargets(targetsDiff) +func newPerNodeStrategy() Strategy { + return &perNodeStrategy{ + collectorByNode: make(map[string]*Collector), } } -// handleTargets receives the new and removed targets and reconciles the current state. -// Any removals are removed from the allocator's targetItems and unassigned from the corresponding collector. -// Any net-new additions are assigned to the collector on the same node as the target. -func (allocator *perNodeAllocator) handleTargets(diff diff.Changes[*target.Item]) { - // Check for removals - for k, item := range allocator.targetItems { - // if the current item is in the removals list - if _, ok := diff.Removals()[k]; ok { - c, ok := allocator.collectors[item.GetNodeName()] - if ok { - c.NumTargets-- - TargetsPerCollector.WithLabelValues(item.CollectorName, perNodeStrategyName).Set(float64(c.NumTargets)) - } - delete(allocator.targetItems, k) - delete(allocator.targetItemsPerJobPerCollector[item.CollectorName][item.JobName], item.Hash()) - } - } - - // Check for additions - var unassignedTargets int - for k, item := range diff.Additions() { - // Do nothing if the item is already there - if _, ok := allocator.targetItems[k]; ok { - continue - } else { - // Add item to item pool and assign a collector - collectorAssigned := allocator.addTargetToTargetItems(item) - if !collectorAssigned { - unassignedTargets++ - } - } - } - - // Check for unassigned targets - if unassignedTargets > 0 { - allocator.log.Info("Could not assign targets for some jobs due to missing node labels", "targets", unassignedTargets) - TargetsUnassigned.Set(float64(unassignedTargets)) - } +func (s *perNodeStrategy) GetName() string { + return perNodeStrategyName } -// addTargetToTargetItems assigns a target to the collector and adds it to the allocator's targetItems -// This method is called from within SetTargets and SetCollectors, which acquire the needed lock. -// This is only called after the collectors are cleared or when a new target has been found in the tempTargetMap. -// Also, any targets that cannot be assigned to a collector, due to no matching node name, will remain unassigned. These -// targets are still "silently" added to the targetItems map, to make sure they exist if collector for a node is added -// later and to prevent them from being reported as unassigned on each new target items setting. -func (allocator *perNodeAllocator) addTargetToTargetItems(tg *target.Item) bool { - allocator.targetItems[tg.Hash()] = tg - chosenCollector, ok := allocator.collectors[tg.GetNodeName()] +func (s *perNodeStrategy) GetCollectorForTarget(collectors map[string]*Collector, item *target.Item) (*Collector, error) { + targetNodeName := item.GetNodeName() + collector, ok := s.collectorByNode[targetNodeName] if !ok { - allocator.log.V(2).Info("Couldn't find a collector for the target item", "item", tg, "collectors", allocator.collectors) - return false - } - tg.CollectorName = chosenCollector.Name - allocator.addCollectorTargetItemMapping(tg) - chosenCollector.NumTargets++ - TargetsPerCollector.WithLabelValues(chosenCollector.Name, perNodeStrategyName).Set(float64(chosenCollector.NumTargets)) - return true -} - -// addCollectorTargetItemMapping keeps track of which collector has which jobs and targets -// this allows the allocator to respond without any extra allocations to http calls. The caller of this method -// has to acquire a lock. -func (allocator *perNodeAllocator) addCollectorTargetItemMapping(tg *target.Item) { - if allocator.targetItemsPerJobPerCollector[tg.CollectorName] == nil { - allocator.targetItemsPerJobPerCollector[tg.CollectorName] = make(map[string]map[string]bool) - } - if allocator.targetItemsPerJobPerCollector[tg.CollectorName][tg.JobName] == nil { - allocator.targetItemsPerJobPerCollector[tg.CollectorName][tg.JobName] = make(map[string]bool) - } - allocator.targetItemsPerJobPerCollector[tg.CollectorName][tg.JobName][tg.Hash()] = true -} - -// TargetItems returns a shallow copy of the targetItems map. -func (allocator *perNodeAllocator) TargetItems() map[string]*target.Item { - allocator.m.RLock() - defer allocator.m.RUnlock() - targetItemsCopy := make(map[string]*target.Item) - for k, v := range allocator.targetItems { - targetItemsCopy[k] = v - } - return targetItemsCopy -} - -// Collectors returns a shallow copy of the collectors map. -func (allocator *perNodeAllocator) Collectors() map[string]*Collector { - allocator.m.RLock() - defer allocator.m.RUnlock() - collectorsCopy := make(map[string]*Collector) - for k, v := range allocator.collectors { - collectorsCopy[k] = v - } - return collectorsCopy -} - -func (allocator *perNodeAllocator) GetTargetsForCollectorAndJob(collector string, job string) []*target.Item { - allocator.m.RLock() - defer allocator.m.RUnlock() - if _, ok := allocator.targetItemsPerJobPerCollector[collector]; !ok { - return []*target.Item{} - } - if _, ok := allocator.targetItemsPerJobPerCollector[collector][job]; !ok { - return []*target.Item{} - } - targetItemsCopy := make([]*target.Item, len(allocator.targetItemsPerJobPerCollector[collector][job])) - index := 0 - for targetHash := range allocator.targetItemsPerJobPerCollector[collector][job] { - targetItemsCopy[index] = allocator.targetItems[targetHash] - index++ + return nil, fmt.Errorf("could not find collector for node %s", targetNodeName) } - return targetItemsCopy + return collectors[collector.Name], nil } -// SetFilter sets the filtering hook to use. -func (allocator *perNodeAllocator) SetFilter(filter Filter) { - allocator.filter = filter -} - -func newPerNodeAllocator(log logr.Logger, opts ...AllocationOption) Allocator { - pnAllocator := &perNodeAllocator{ - log: log, - collectors: make(map[string]*Collector), - targetItems: make(map[string]*target.Item), - targetItemsPerJobPerCollector: make(map[string]map[string]map[string]bool), - } - - for _, opt := range opts { - opt(pnAllocator) +func (s *perNodeStrategy) SetCollectors(collectors map[string]*Collector) { + clear(s.collectorByNode) + for _, collector := range collectors { + if collector.NodeName != "" { + s.collectorByNode[collector.NodeName] = collector + } } - - return pnAllocator } diff --git a/cmd/otel-allocator/allocation/per_node_test.go b/cmd/otel-allocator/allocation/per_node_test.go index 047e16dc6d..d853574a11 100644 --- a/cmd/otel-allocator/allocation/per_node_test.go +++ b/cmd/otel-allocator/allocation/per_node_test.go @@ -128,7 +128,7 @@ func TestTargetsWithNoCollectorsPerNode(t *testing.T) { assert.Len(t, actualCollectors, numCols) // Based on lable all targets should be assigned to node-0 for name, ac := range actualCollectors { - if name == "node-0" { + if name == "collector-0" { assert.Equal(t, 6, ac.NumTargets) } else { assert.Equal(t, 0, ac.NumTargets) diff --git a/cmd/otel-allocator/allocation/strategy.go b/cmd/otel-allocator/allocation/strategy.go index b61313bd1f..29ae7fd99a 100644 --- a/cmd/otel-allocator/allocation/strategy.go +++ b/cmd/otel-allocator/allocation/strategy.go @@ -103,6 +103,15 @@ type Allocator interface { SetFilter(filter Filter) } +type Strategy interface { + GetCollectorForTarget(map[string]*Collector, *target.Item) (*Collector, error) + // SetCollectors exists for strategies where changing the collector set is potentially an expensive operation. + // The caller must guarantee that the collectors map passed in GetCollectorForTarget is consistent with the latest + // SetCollectors call. Strategies which don't need this information can just ignore it. + SetCollectors(map[string]*Collector) + GetName() string +} + var _ consistent.Member = Collector{} // Collector Creates a struct that holds Collector information. @@ -127,15 +136,21 @@ func NewCollector(name, node string) *Collector { } func init() { - err := Register(leastWeightedStrategyName, newLeastWeightedAllocator) + err := Register(leastWeightedStrategyName, func(log logr.Logger, opts ...AllocationOption) Allocator { + return newAllocator(log, newleastWeightedStrategy(), opts...) + }) if err != nil { panic(err) } - err = Register(consistentHashingStrategyName, newConsistentHashingAllocator) + err = Register(consistentHashingStrategyName, func(log logr.Logger, opts ...AllocationOption) Allocator { + return newAllocator(log, newConsistentHashingStrategy(), opts...) + }) if err != nil { panic(err) } - err = Register(perNodeStrategyName, newPerNodeAllocator) + err = Register(perNodeStrategyName, func(log logr.Logger, opts ...AllocationOption) Allocator { + return newAllocator(log, newPerNodeStrategy(), opts...) + }) if err != nil { panic(err) } diff --git a/cmd/otel-allocator/allocation/allocatortest.go b/cmd/otel-allocator/allocation/testutils.go similarity index 80% rename from cmd/otel-allocator/allocation/allocatortest.go rename to cmd/otel-allocator/allocation/testutils.go index c47f5976ce..054e9e0205 100644 --- a/cmd/otel-allocator/allocation/allocatortest.go +++ b/cmd/otel-allocator/allocation/testutils.go @@ -12,13 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. +// Note: These utilities are used by other packages, which is why they're defined in a non-test file. + package allocation import ( "fmt" "strconv" + "testing" "github.com/prometheus/common/model" + "github.com/stretchr/testify/require" + logf "sigs.k8s.io/controller-runtime/pkg/log" "github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator/target" ) @@ -70,3 +75,15 @@ func MakeNNewTargetsWithEmptyCollectors(n int, startingIndex int) map[string]*ta } return toReturn } + +func RunForAllStrategies(t *testing.T, f func(t *testing.T, allocator Allocator)) { + allocatorNames := GetRegisteredAllocatorNames() + logger := logf.Log.WithName("unit-tests") + for _, allocatorName := range allocatorNames { + t.Run(allocatorName, func(t *testing.T) { + allocator, err := New(allocatorName, logger) + require.NoError(t, err) + f(t, allocator) + }) + } +} diff --git a/controllers/builder_test.go b/controllers/builder_test.go index 5cf49c9cde..32fd6db2fd 100644 --- a/controllers/builder_test.go +++ b/controllers/builder_test.go @@ -259,12 +259,13 @@ service: Name: "test-collector", Namespace: "test", Labels: map[string]string{ - "app.kubernetes.io/component": "opentelemetry-collector", - "app.kubernetes.io/instance": "test.test", - "app.kubernetes.io/managed-by": "opentelemetry-operator", - "app.kubernetes.io/name": "test-collector", - "app.kubernetes.io/part-of": "opentelemetry", - "app.kubernetes.io/version": "latest", + "app.kubernetes.io/component": "opentelemetry-collector", + "app.kubernetes.io/instance": "test.test", + "app.kubernetes.io/managed-by": "opentelemetry-operator", + "app.kubernetes.io/name": "test-collector", + "app.kubernetes.io/part-of": "opentelemetry", + "app.kubernetes.io/version": "latest", + "operator.opentelemetry.io/collector-service-type": "base", }, Annotations: nil, }, @@ -291,6 +292,7 @@ service: "app.kubernetes.io/part-of": "opentelemetry", "app.kubernetes.io/version": "latest", "operator.opentelemetry.io/collector-headless-service": "Exists", + "operator.opentelemetry.io/collector-service-type": "headless", }, Annotations: map[string]string{ "service.beta.openshift.io/serving-cert-secret-name": "test-collector-headless-tls", @@ -319,6 +321,7 @@ service: "app.kubernetes.io/name": "test-collector-monitoring", "app.kubernetes.io/part-of": "opentelemetry", "app.kubernetes.io/version": "latest", + "operator.opentelemetry.io/collector-service-type": "monitoring", "operator.opentelemetry.io/collector-monitoring-service": "Exists", }, Annotations: nil, @@ -506,12 +509,13 @@ service: Name: "test-collector", Namespace: "test", Labels: map[string]string{ - "app.kubernetes.io/component": "opentelemetry-collector", - "app.kubernetes.io/instance": "test.test", - "app.kubernetes.io/managed-by": "opentelemetry-operator", - "app.kubernetes.io/name": "test-collector", - "app.kubernetes.io/part-of": "opentelemetry", - "app.kubernetes.io/version": "latest", + "app.kubernetes.io/component": "opentelemetry-collector", + "app.kubernetes.io/instance": "test.test", + "app.kubernetes.io/managed-by": "opentelemetry-operator", + "app.kubernetes.io/name": "test-collector", + "app.kubernetes.io/part-of": "opentelemetry", + "app.kubernetes.io/version": "latest", + "operator.opentelemetry.io/collector-service-type": "base", }, Annotations: nil, }, @@ -537,6 +541,7 @@ service: "app.kubernetes.io/name": "test-collector", "app.kubernetes.io/part-of": "opentelemetry", "app.kubernetes.io/version": "latest", + "operator.opentelemetry.io/collector-service-type": "headless", "operator.opentelemetry.io/collector-headless-service": "Exists", }, Annotations: map[string]string{ @@ -566,6 +571,7 @@ service: "app.kubernetes.io/name": "test-collector-monitoring", "app.kubernetes.io/part-of": "opentelemetry", "app.kubernetes.io/version": "latest", + "operator.opentelemetry.io/collector-service-type": "monitoring", "operator.opentelemetry.io/collector-monitoring-service": "Exists", }, Annotations: nil, @@ -774,12 +780,13 @@ service: Name: "test-collector", Namespace: "test", Labels: map[string]string{ - "app.kubernetes.io/component": "opentelemetry-collector", - "app.kubernetes.io/instance": "test.test", - "app.kubernetes.io/managed-by": "opentelemetry-operator", - "app.kubernetes.io/name": "test-collector", - "app.kubernetes.io/part-of": "opentelemetry", - "app.kubernetes.io/version": "latest", + "app.kubernetes.io/component": "opentelemetry-collector", + "app.kubernetes.io/instance": "test.test", + "app.kubernetes.io/managed-by": "opentelemetry-operator", + "app.kubernetes.io/name": "test-collector", + "app.kubernetes.io/part-of": "opentelemetry", + "app.kubernetes.io/version": "latest", + "operator.opentelemetry.io/collector-service-type": "base", }, Annotations: nil, }, @@ -805,6 +812,7 @@ service: "app.kubernetes.io/name": "test-collector", "app.kubernetes.io/part-of": "opentelemetry", "app.kubernetes.io/version": "latest", + "operator.opentelemetry.io/collector-service-type": "headless", "operator.opentelemetry.io/collector-headless-service": "Exists", }, Annotations: map[string]string{ @@ -834,6 +842,7 @@ service: "app.kubernetes.io/name": "test-collector-monitoring", "app.kubernetes.io/part-of": "opentelemetry", "app.kubernetes.io/version": "latest", + "operator.opentelemetry.io/collector-service-type": "monitoring", "operator.opentelemetry.io/collector-monitoring-service": "Exists", }, Annotations: nil, @@ -1317,6 +1326,7 @@ service: "app.kubernetes.io/name": "test-collector-monitoring", "app.kubernetes.io/part-of": "opentelemetry", "app.kubernetes.io/version": "latest", + "operator.opentelemetry.io/collector-service-type": "monitoring", "operator.opentelemetry.io/collector-monitoring-service": "Exists", }, Annotations: nil, @@ -1711,6 +1721,7 @@ prometheus_cr: "app.kubernetes.io/name": "test-collector-monitoring", "app.kubernetes.io/part-of": "opentelemetry", "app.kubernetes.io/version": "latest", + "operator.opentelemetry.io/collector-service-type": "monitoring", "operator.opentelemetry.io/collector-monitoring-service": "Exists", }, Annotations: nil, diff --git a/internal/manifests/collector/collector.go b/internal/manifests/collector/collector.go index 9cb2302bba..65a19cdc9e 100644 --- a/internal/manifests/collector/collector.go +++ b/internal/manifests/collector/collector.go @@ -57,7 +57,7 @@ func Build(params manifests.Params) ([]client.Object, error) { if params.OtelCol.Spec.Mode == v1beta1.ModeSidecar { manifestFactories = append(manifestFactories, manifests.Factory(PodMonitor)) } else { - manifestFactories = append(manifestFactories, manifests.Factory(ServiceMonitor)) + manifestFactories = append(manifestFactories, manifests.Factory(ServiceMonitor), manifests.Factory(ServiceMonitorMonitoring)) } } diff --git a/internal/manifests/collector/podmonitor.go b/internal/manifests/collector/podmonitor.go index 86157c4138..761f7d307c 100644 --- a/internal/manifests/collector/podmonitor.go +++ b/internal/manifests/collector/podmonitor.go @@ -31,28 +31,14 @@ import ( // PodMonitor returns the pod monitor for the given instance. func PodMonitor(params manifests.Params) (*monitoringv1.PodMonitor, error) { - if !params.OtelCol.Spec.Observability.Metrics.EnableMetrics { - params.Log.V(2).Info("Metrics disabled for this OTEL Collector", - "params.OtelCol.name", params.OtelCol.Name, - "params.OtelCol.namespace", params.OtelCol.Namespace, - ) - return nil, nil - } else if params.Config.PrometheusCRAvailability() == prometheus.NotAvailable { - params.Log.V(1).Info("Cannot enable PodMonitor when prometheus CRDs are unavailable", - "params.OtelCol.name", params.OtelCol.Name, - "params.OtelCol.namespace", params.OtelCol.Namespace, - ) + if !shouldCreatePodMonitor(params) { return nil, nil } - var pm monitoringv1.PodMonitor - if params.OtelCol.Spec.Mode != v1beta1.ModeSidecar { - return nil, nil - } name := naming.PodMonitor(params.OtelCol.Name) labels := manifestutils.Labels(params.OtelCol.ObjectMeta, name, params.OtelCol.Spec.Image, ComponentOpenTelemetryCollector, nil) selectorLabels := manifestutils.SelectorLabels(params.OtelCol.ObjectMeta, ComponentOpenTelemetryCollector) - pm = monitoringv1.PodMonitor{ + pm := monitoringv1.PodMonitor{ ObjectMeta: metav1.ObjectMeta{ Namespace: params.OtelCol.Namespace, Name: name, @@ -107,3 +93,22 @@ func metricsEndpointsFromConfig(logger logr.Logger, otelcol v1beta1.OpenTelemetr } return metricsEndpoints } + +func shouldCreatePodMonitor(params manifests.Params) bool { + l := params.Log.WithValues( + "params.OtelCol.name", params.OtelCol.Name, + "params.OtelCol.namespace", params.OtelCol.Namespace, + ) + + if !params.OtelCol.Spec.Observability.Metrics.EnableMetrics { + l.V(2).Info("Metrics disabled for this OTEL Collector. PodMonitor will not ve created") + return false + } else if params.Config.PrometheusCRAvailability() == prometheus.NotAvailable { + l.V(2).Info("Cannot enable PodMonitor when prometheus CRDs are unavailable") + return false + } else if params.OtelCol.Spec.Mode != v1beta1.ModeSidecar { + l.V(2).Info("Not using sidecar mode. PodMonitor will not be created") + return false + } + return true +} diff --git a/internal/manifests/collector/service.go b/internal/manifests/collector/service.go index 10d3ae15ab..d66e4bfe99 100644 --- a/internal/manifests/collector/service.go +++ b/internal/manifests/collector/service.go @@ -29,13 +29,26 @@ import ( "github.com/open-telemetry/opentelemetry-operator/internal/naming" ) -// headless and monitoring labels are to differentiate the headless/monitoring services from the clusterIP service. +// headless and monitoring labels are to differentiate the base/headless/monitoring services from the clusterIP service. const ( - headlessLabel = "operator.opentelemetry.io/collector-headless-service" - monitoringLabel = "operator.opentelemetry.io/collector-monitoring-service" - valueExists = "Exists" + headlessLabel = "operator.opentelemetry.io/collector-headless-service" + monitoringLabel = "operator.opentelemetry.io/collector-monitoring-service" + serviceTypeLabel = "operator.opentelemetry.io/collector-service-type" + valueExists = "Exists" ) +type ServiceType int + +const ( + BaseServiceType ServiceType = iota + HeadlessServiceType + MonitoringServiceType +) + +func (s ServiceType) String() string { + return [...]string{"base", "headless", "monitoring"}[s] +} + func HeadlessService(params manifests.Params) (*corev1.Service, error) { h, err := Service(params) if h == nil || err != nil { @@ -44,6 +57,7 @@ func HeadlessService(params manifests.Params) (*corev1.Service, error) { h.Name = naming.HeadlessService(params.OtelCol.Name) h.Labels[headlessLabel] = valueExists + h.Labels[serviceTypeLabel] = HeadlessServiceType.String() // copy to avoid modifying params.OtelCol.Annotations annotations := map[string]string{ @@ -63,6 +77,7 @@ func MonitoringService(params manifests.Params) (*corev1.Service, error) { name := naming.MonitoringService(params.OtelCol.Name) labels := manifestutils.Labels(params.OtelCol.ObjectMeta, name, params.OtelCol.Spec.Image, ComponentOpenTelemetryCollector, []string{}) labels[monitoringLabel] = valueExists + labels[serviceTypeLabel] = MonitoringServiceType.String() metricsPort, err := params.OtelCol.Spec.Config.Service.MetricsPort() if err != nil { @@ -90,6 +105,7 @@ func MonitoringService(params manifests.Params) (*corev1.Service, error) { func Service(params manifests.Params) (*corev1.Service, error) { name := naming.Service(params.OtelCol.Name) labels := manifestutils.Labels(params.OtelCol.ObjectMeta, name, params.OtelCol.Spec.Image, ComponentOpenTelemetryCollector, []string{}) + labels[serviceTypeLabel] = BaseServiceType.String() out, err := params.OtelCol.Spec.Config.Yaml() if err != nil { diff --git a/internal/manifests/collector/service_test.go b/internal/manifests/collector/service_test.go index 0e3c125be5..2a5cd8d08f 100644 --- a/internal/manifests/collector/service_test.go +++ b/internal/manifests/collector/service_test.go @@ -286,6 +286,7 @@ func service(name string, ports []v1beta1.PortsSpec) v1.Service { func serviceWithInternalTrafficPolicy(name string, ports []v1beta1.PortsSpec, internalTrafficPolicy v1.ServiceInternalTrafficPolicyType) v1.Service { params := deploymentParams() labels := manifestutils.Labels(params.OtelCol.ObjectMeta, name, params.OtelCol.Spec.Image, ComponentOpenTelemetryCollector, []string{}) + labels[serviceTypeLabel] = BaseServiceType.String() svcPorts := []v1.ServicePort{} for _, p := range ports { diff --git a/internal/manifests/collector/servicemonitor.go b/internal/manifests/collector/servicemonitor.go index 1713ccfe50..2c1088f44c 100644 --- a/internal/manifests/collector/servicemonitor.go +++ b/internal/manifests/collector/servicemonitor.go @@ -15,6 +15,7 @@ package collector import ( + "fmt" "strings" "github.com/go-logr/logr" @@ -29,30 +30,40 @@ import ( "github.com/open-telemetry/opentelemetry-operator/internal/naming" ) -// ServiceMonitor returns the service monitor for the given instance. +// ServiceMonitor returns the service monitor for the collector. func ServiceMonitor(params manifests.Params) (*monitoringv1.ServiceMonitor, error) { - if !params.OtelCol.Spec.Observability.Metrics.EnableMetrics { - params.Log.V(2).Info("Metrics disabled for this OTEL Collector", - "params.OtelCol.name", params.OtelCol.Name, - "params.OtelCol.namespace", params.OtelCol.Namespace, - ) - return nil, nil - } else if params.Config.PrometheusCRAvailability() == prometheus.NotAvailable { - params.Log.V(1).Info("Cannot enable ServiceMonitor when prometheus CRDs are unavailable", - "params.OtelCol.name", params.OtelCol.Name, - "params.OtelCol.namespace", params.OtelCol.Namespace, - ) - return nil, nil + name := naming.ServiceMonitor(params.OtelCol.Name) + endpoints := endpointsFromConfig(params.Log, params.OtelCol) + if len(endpoints) > 0 { + return createServiceMonitor(name, params, BaseServiceType, endpoints) } - var sm monitoringv1.ServiceMonitor + return nil, nil +} + +// ServiceMonitor returns the service monitor for the monitoring service of the collector. +func ServiceMonitorMonitoring(params manifests.Params) (*monitoringv1.ServiceMonitor, error) { + name := naming.ServiceMonitor(fmt.Sprintf("%s-monitoring", params.OtelCol.Name)) + endpoints := []monitoringv1.Endpoint{ + { + Port: "monitoring", + }, + } + return createServiceMonitor(name, params, MonitoringServiceType, endpoints) +} - if params.OtelCol.Spec.Mode == v1beta1.ModeSidecar { +// createServiceMonitor creates a Service Monitor using the provided name, the params from the instance, a label to identify the service +// to target (like the monitoring or the collector services) and the endpoints to scrape. +func createServiceMonitor(name string, params manifests.Params, serviceType ServiceType, endpoints []monitoringv1.Endpoint) (*monitoringv1.ServiceMonitor, error) { + if !shouldCreateServiceMonitor(params) { return nil, nil } - name := naming.ServiceMonitor(params.OtelCol.Name) + + var sm monitoringv1.ServiceMonitor + labels := manifestutils.Labels(params.OtelCol.ObjectMeta, name, params.OtelCol.Spec.Image, ComponentOpenTelemetryCollector, []string{}) selectorLabels := manifestutils.SelectorLabels(params.OtelCol.ObjectMeta, ComponentOpenTelemetryCollector) - selectorLabels[monitoringLabel] = valueExists + // This label is the one which differentiates the services + selectorLabels[serviceTypeLabel] = serviceType.String() sm = monitoringv1.ServiceMonitor{ ObjectMeta: metav1.ObjectMeta{ @@ -61,11 +72,7 @@ func ServiceMonitor(params manifests.Params) (*monitoringv1.ServiceMonitor, erro Labels: labels, }, Spec: monitoringv1.ServiceMonitorSpec{ - Endpoints: append([]monitoringv1.Endpoint{ - { - Port: "monitoring", - }, - }, endpointsFromConfig(params.Log, params.OtelCol)...), + Endpoints: endpoints, NamespaceSelector: monitoringv1.NamespaceSelector{ MatchNames: []string{params.OtelCol.Namespace}, }, @@ -78,6 +85,25 @@ func ServiceMonitor(params manifests.Params) (*monitoringv1.ServiceMonitor, erro return &sm, nil } +func shouldCreateServiceMonitor(params manifests.Params) bool { + l := params.Log.WithValues( + "params.OtelCol.name", params.OtelCol.Name, + "params.OtelCol.namespace", params.OtelCol.Namespace, + ) + + if !params.OtelCol.Spec.Observability.Metrics.EnableMetrics { + l.V(2).Info("Metrics disabled for this OTEL Collector. ServiceMonitor will not ve created") + return false + } else if params.Config.PrometheusCRAvailability() == prometheus.NotAvailable { + l.V(2).Info("Cannot enable ServiceMonitor when prometheus CRDs are unavailable") + return false + } else if params.OtelCol.Spec.Mode == v1beta1.ModeSidecar { + l.V(2).Info("Using sidecar mode. ServiceMonitor will not be created") + return false + } + return true +} + func endpointsFromConfig(logger logr.Logger, otelcol v1beta1.OpenTelemetryCollector) []monitoringv1.Endpoint { // TODO: https://github.com/open-telemetry/opentelemetry-operator/issues/2603 cfgStr, err := otelcol.Spec.Config.Yaml() diff --git a/internal/manifests/collector/servicemonitor_test.go b/internal/manifests/collector/servicemonitor_test.go index 8b0cc7f117..63f1b216c2 100644 --- a/internal/manifests/collector/servicemonitor_test.go +++ b/internal/manifests/collector/servicemonitor_test.go @@ -34,18 +34,24 @@ func TestDesiredServiceMonitors(t *testing.T) { params.OtelCol.Spec.Observability.Metrics.EnableMetrics = true actual, err = ServiceMonitor(params) assert.NoError(t, err) + assert.Nil(t, actual) + + // Check the monitoring SM + actual, err = ServiceMonitorMonitoring(params) + assert.NoError(t, err) assert.NotNil(t, actual) - assert.Equal(t, fmt.Sprintf("%s-collector", params.OtelCol.Name), actual.Name) + assert.Equal(t, fmt.Sprintf("%s-monitoring-collector", params.OtelCol.Name), actual.Name) assert.Equal(t, params.OtelCol.Namespace, actual.Namespace) assert.Equal(t, "monitoring", actual.Spec.Endpoints[0].Port) - expectedSelectorLabels := map[string]string{ - "app.kubernetes.io/component": "opentelemetry-collector", - "app.kubernetes.io/instance": "default.test", - "app.kubernetes.io/managed-by": "opentelemetry-operator", - "app.kubernetes.io/part-of": "opentelemetry", - "operator.opentelemetry.io/collector-monitoring-service": "Exists", + expectedSelectorLabelsMonitor := map[string]string{ + "app.kubernetes.io/component": "opentelemetry-collector", + "app.kubernetes.io/instance": "default.test", + "app.kubernetes.io/managed-by": "opentelemetry-operator", + "app.kubernetes.io/part-of": "opentelemetry", + "operator.opentelemetry.io/collector-service-type": "monitoring", } - assert.Equal(t, expectedSelectorLabels, actual.Spec.Selector.MatchLabels) + assert.Equal(t, expectedSelectorLabelsMonitor, actual.Spec.Selector.MatchLabels) + } func TestDesiredServiceMonitorsWithPrometheus(t *testing.T) { @@ -57,15 +63,14 @@ func TestDesiredServiceMonitorsWithPrometheus(t *testing.T) { assert.NotNil(t, actual) assert.Equal(t, fmt.Sprintf("%s-collector", params.OtelCol.Name), actual.Name) assert.Equal(t, params.OtelCol.Namespace, actual.Namespace) - assert.Equal(t, "monitoring", actual.Spec.Endpoints[0].Port) - assert.Equal(t, "prometheus-dev", actual.Spec.Endpoints[1].Port) - assert.Equal(t, "prometheus-prod", actual.Spec.Endpoints[2].Port) + assert.Equal(t, "prometheus-dev", actual.Spec.Endpoints[0].Port) + assert.Equal(t, "prometheus-prod", actual.Spec.Endpoints[1].Port) expectedSelectorLabels := map[string]string{ - "app.kubernetes.io/component": "opentelemetry-collector", - "app.kubernetes.io/instance": "default.test", - "app.kubernetes.io/managed-by": "opentelemetry-operator", - "app.kubernetes.io/part-of": "opentelemetry", - "operator.opentelemetry.io/collector-monitoring-service": "Exists", + "app.kubernetes.io/component": "opentelemetry-collector", + "app.kubernetes.io/instance": "default.test", + "app.kubernetes.io/managed-by": "opentelemetry-operator", + "app.kubernetes.io/part-of": "opentelemetry", + "operator.opentelemetry.io/collector-service-type": "base", } assert.Equal(t, expectedSelectorLabels, actual.Spec.Selector.MatchLabels) } diff --git a/tests/e2e-openshift/kafka/03-assert.yaml b/tests/e2e-openshift/kafka/03-assert.yaml index 0152057fd0..34cfabbea3 100644 --- a/tests/e2e-openshift/kafka/03-assert.yaml +++ b/tests/e2e-openshift/kafka/03-assert.yaml @@ -53,6 +53,7 @@ metadata: app.kubernetes.io/instance: chainsaw-kafka.kafka-exporter app.kubernetes.io/managed-by: opentelemetry-operator app.kubernetes.io/name: kafka-exporter-collector + operator.opentelemetry.io/collector-service-type: headless operator.opentelemetry.io/collector-headless-service: Exists name: kafka-exporter-collector-headless namespace: chainsaw-kafka diff --git a/tests/e2e-openshift/monitoring/01-assert.yaml b/tests/e2e-openshift/monitoring/01-assert.yaml index 170c0f4148..aefeb5e82d 100644 --- a/tests/e2e-openshift/monitoring/01-assert.yaml +++ b/tests/e2e-openshift/monitoring/01-assert.yaml @@ -15,22 +15,21 @@ status: availableReplicas: 1 readyReplicas: 1 replicas: 1 - --- apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: labels: app.kubernetes.io/managed-by: opentelemetry-operator - app.kubernetes.io/name: cluster-collector-collector - name: cluster-collector-collector + app.kubernetes.io/name: cluster-collector-monitoring-collector + name: cluster-collector-monitoring-collector spec: endpoints: - port: monitoring selector: matchLabels: app.kubernetes.io/managed-by: opentelemetry-operator - + operator.opentelemetry.io/collector-service-type: monitoring --- apiVersion: v1 kind: Service @@ -68,6 +67,7 @@ metadata: app.kubernetes.io/managed-by: opentelemetry-operator app.kubernetes.io/name: cluster-collector-collector app.kubernetes.io/part-of: opentelemetry + operator.opentelemetry.io/collector-service-type: headless operator.opentelemetry.io/collector-headless-service: Exists name: cluster-collector-collector-headless spec: @@ -87,7 +87,6 @@ spec: app.kubernetes.io/managed-by: opentelemetry-operator app.kubernetes.io/part-of: opentelemetry type: ClusterIP - --- apiVersion: v1 kind: Service diff --git a/tests/e2e-openshift/monitoring/04-assert.yaml b/tests/e2e-openshift/monitoring/04-assert.yaml new file mode 100644 index 0000000000..0b72375c3b --- /dev/null +++ b/tests/e2e-openshift/monitoring/04-assert.yaml @@ -0,0 +1,14 @@ +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + labels: + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/name: cluster-collector2-collector + name: cluster-collector2-collector +spec: + endpoints: + - port: prometheus + selector: + matchLabels: + app.kubernetes.io/managed-by: opentelemetry-operator + operator.opentelemetry.io/collector-service-type: base diff --git a/tests/e2e-openshift/monitoring/04-use-prometheus-exporter.yaml b/tests/e2e-openshift/monitoring/04-use-prometheus-exporter.yaml new file mode 100644 index 0000000000..4eb57f2c9a --- /dev/null +++ b/tests/e2e-openshift/monitoring/04-use-prometheus-exporter.yaml @@ -0,0 +1,25 @@ +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: cluster-collector2 +spec: + mode: deployment + observability: + metrics: + enableMetrics: true + config: | + receivers: + otlp: + protocols: + grpc: + http: + processors: + exporters: + prometheus: + endpoint: "0.0.0.0:8091" + service: + pipelines: + metrics: + receivers: [otlp] + processors: [] + exporters: [prometheus] diff --git a/tests/e2e-openshift/monitoring/chainsaw-test.yaml b/tests/e2e-openshift/monitoring/chainsaw-test.yaml index 232e9a2522..0cf36e93f0 100755 --- a/tests/e2e-openshift/monitoring/chainsaw-test.yaml +++ b/tests/e2e-openshift/monitoring/chainsaw-test.yaml @@ -35,3 +35,9 @@ spec: - script: timeout: 5m content: ./check_metrics.sh + - name: step-04 + try: + - apply: + file: 04-use-prometheus-exporter.yaml + - assert: + file: 04-assert.yaml \ No newline at end of file diff --git a/tests/e2e-openshift/multi-cluster/02-assert.yaml b/tests/e2e-openshift/multi-cluster/02-assert.yaml index 7ba79cbe56..c4dae4d27f 100644 --- a/tests/e2e-openshift/multi-cluster/02-assert.yaml +++ b/tests/e2e-openshift/multi-cluster/02-assert.yaml @@ -58,6 +58,7 @@ metadata: app.kubernetes.io/name: otlp-receiver-collector app.kubernetes.io/part-of: opentelemetry app.kubernetes.io/version: latest + operator.opentelemetry.io/collector-service-type: headless operator.opentelemetry.io/collector-headless-service: Exists name: otlp-receiver-collector-headless namespace: chainsaw-multi-cluster-receive diff --git a/tests/e2e-openshift/multi-cluster/03-assert.yaml b/tests/e2e-openshift/multi-cluster/03-assert.yaml index bc3a130380..a22efdb841 100644 --- a/tests/e2e-openshift/multi-cluster/03-assert.yaml +++ b/tests/e2e-openshift/multi-cluster/03-assert.yaml @@ -58,6 +58,7 @@ metadata: app.kubernetes.io/name: otel-sender-collector app.kubernetes.io/part-of: opentelemetry app.kubernetes.io/version: latest + operator.opentelemetry.io/collector-service-type: headless operator.opentelemetry.io/collector-headless-service: Exists name: otel-sender-collector-headless namespace: chainsaw-multi-cluster-send diff --git a/tests/e2e-openshift/otlp-metrics-traces/02-assert.yaml b/tests/e2e-openshift/otlp-metrics-traces/02-assert.yaml index 057820e934..c2403ae18f 100644 --- a/tests/e2e-openshift/otlp-metrics-traces/02-assert.yaml +++ b/tests/e2e-openshift/otlp-metrics-traces/02-assert.yaml @@ -11,6 +11,27 @@ status: --- apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor +metadata: + labels: + app.kubernetes.io/instance: chainsaw-otlp-metrics.cluster-collector + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/name: cluster-collector-monitoring-collector + name: cluster-collector-monitoring-collector + namespace: chainsaw-otlp-metrics +spec: + endpoints: + - port: monitoring + namespaceSelector: + matchNames: + - chainsaw-otlp-metrics + selector: + matchLabels: + app.kubernetes.io/instance: chainsaw-otlp-metrics.cluster-collector + app.kubernetes.io/managed-by: opentelemetry-operator + operator.opentelemetry.io/collector-service-type: monitoring +--- +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor metadata: labels: app.kubernetes.io/instance: chainsaw-otlp-metrics.cluster-collector @@ -20,7 +41,6 @@ metadata: namespace: chainsaw-otlp-metrics spec: endpoints: - - port: monitoring - port: prometheus namespaceSelector: matchNames: @@ -29,3 +49,4 @@ spec: matchLabels: app.kubernetes.io/instance: chainsaw-otlp-metrics.cluster-collector app.kubernetes.io/managed-by: opentelemetry-operator + operator.opentelemetry.io/collector-service-type: base \ No newline at end of file diff --git a/tests/e2e-prometheuscr/create-sm-prometheus-exporters/01-assert.yaml b/tests/e2e-prometheuscr/create-sm-prometheus-exporters/01-assert.yaml index eb0652f517..0b2bd613d3 100644 --- a/tests/e2e-prometheuscr/create-sm-prometheus-exporters/01-assert.yaml +++ b/tests/e2e-prometheuscr/create-sm-prometheus-exporters/01-assert.yaml @@ -1,5 +1,28 @@ apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor +metadata: + labels: + app.kubernetes.io/instance: create-sm-prometheus.simplest + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/name: simplest-monitoring-collector + name: simplest-monitoring-collector + namespace: create-sm-prometheus +spec: + endpoints: + - port: monitoring + namespaceSelector: + matchNames: + - create-sm-prometheus + selector: + matchLabels: + app.kubernetes.io/component: opentelemetry-collector + app.kubernetes.io/instance: create-sm-prometheus.simplest + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/part-of: opentelemetry + operator.opentelemetry.io/collector-service-type: monitoring +--- +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor metadata: labels: app.kubernetes.io/instance: create-sm-prometheus.simplest @@ -9,7 +32,6 @@ metadata: namespace: create-sm-prometheus spec: endpoints: - - port: monitoring - port: prometheus-dev - port: prometheus-prod namespaceSelector: @@ -21,7 +43,7 @@ spec: app.kubernetes.io/instance: create-sm-prometheus.simplest app.kubernetes.io/managed-by: opentelemetry-operator app.kubernetes.io/part-of: opentelemetry - operator.opentelemetry.io/collector-monitoring-service: "Exists" + operator.opentelemetry.io/collector-service-type: base --- apiVersion: v1 kind: Service @@ -58,7 +80,7 @@ metadata: app.kubernetes.io/managed-by: opentelemetry-operator app.kubernetes.io/name: simplest-collector-monitoring app.kubernetes.io/part-of: opentelemetry - operator.opentelemetry.io/collector-monitoring-service: "Exists" + operator.opentelemetry.io/collector-service-type: "monitoring" name: simplest-collector-monitoring namespace: create-sm-prometheus spec: diff --git a/tests/e2e-prometheuscr/create-sm-prometheus-exporters/02-assert.yaml b/tests/e2e-prometheuscr/create-sm-prometheus-exporters/02-assert.yaml index 4c5b8bd5b8..61f74dd1e1 100644 --- a/tests/e2e-prometheuscr/create-sm-prometheus-exporters/02-assert.yaml +++ b/tests/e2e-prometheuscr/create-sm-prometheus-exporters/02-assert.yaml @@ -1,5 +1,28 @@ apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor +metadata: + labels: + app.kubernetes.io/instance: create-sm-prometheus.simplest + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/name: simplest-monitoring-collector + name: simplest-monitoring-collector + namespace: create-sm-prometheus +spec: + endpoints: + - port: monitoring + namespaceSelector: + matchNames: + - create-sm-prometheus + selector: + matchLabels: + app.kubernetes.io/component: opentelemetry-collector + app.kubernetes.io/instance: create-sm-prometheus.simplest + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/part-of: opentelemetry + operator.opentelemetry.io/collector-service-type: monitoring +--- +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor metadata: labels: app.kubernetes.io/instance: create-sm-prometheus.simplest @@ -9,7 +32,6 @@ metadata: namespace: create-sm-prometheus spec: endpoints: - - port: monitoring - port: prometheus-prod namespaceSelector: matchNames: @@ -20,8 +42,7 @@ spec: app.kubernetes.io/instance: create-sm-prometheus.simplest app.kubernetes.io/managed-by: opentelemetry-operator app.kubernetes.io/part-of: opentelemetry - operator.opentelemetry.io/collector-monitoring-service: "Exists" - + operator.opentelemetry.io/collector-service-type: base --- apiVersion: v1 kind: Service @@ -54,6 +75,7 @@ metadata: app.kubernetes.io/managed-by: opentelemetry-operator app.kubernetes.io/name: simplest-collector-monitoring app.kubernetes.io/part-of: opentelemetry + operator.opentelemetry.io/collector-service-type: "monitoring" operator.opentelemetry.io/collector-monitoring-service: "Exists" name: simplest-collector-monitoring namespace: create-sm-prometheus diff --git a/tests/e2e-prometheuscr/create-sm-prometheus-exporters/04-error.yaml b/tests/e2e-prometheuscr/create-sm-prometheus-exporters/04-error.yaml index 61ad50e38b..263dbb3c64 100644 --- a/tests/e2e-prometheuscr/create-sm-prometheus-exporters/04-error.yaml +++ b/tests/e2e-prometheuscr/create-sm-prometheus-exporters/04-error.yaml @@ -1,19 +1,5 @@ apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: - labels: - app.kubernetes.io/instance: create-sm-prometheus.simplest - app.kubernetes.io/managed-by: opentelemetry-operator - app.kubernetes.io/name: simplest-collector name: simplest-collector namespace: create-sm-prometheus -spec: - endpoints: - - port: monitoring - - port: prometheus-prod - namespaceSelector: - matchNames: - - create-sm-prometheus - selector: - matchLabels: - app.kubernetes.io/managed-by: opentelemetry-operator diff --git a/tests/e2e-prometheuscr/create-sm-prometheus-exporters/05-assert.yaml b/tests/e2e-prometheuscr/create-sm-prometheus-exporters/05-assert.yaml index 3e8205803c..a0b8ebcda6 100644 --- a/tests/e2e-prometheuscr/create-sm-prometheus-exporters/05-assert.yaml +++ b/tests/e2e-prometheuscr/create-sm-prometheus-exporters/05-assert.yaml @@ -1,5 +1,28 @@ apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor +metadata: + labels: + app.kubernetes.io/instance: create-sm-prometheus.simplest + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/name: simplest-monitoring-collector + name: simplest-monitoring-collector + namespace: create-sm-prometheus +spec: + endpoints: + - port: monitoring + namespaceSelector: + matchNames: + - create-sm-prometheus + selector: + matchLabels: + app.kubernetes.io/component: opentelemetry-collector + app.kubernetes.io/instance: create-sm-prometheus.simplest + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/part-of: opentelemetry + operator.opentelemetry.io/collector-service-type: monitoring +--- +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor metadata: labels: app.kubernetes.io/instance: create-sm-prometheus.simplest @@ -9,7 +32,6 @@ metadata: namespace: create-sm-prometheus spec: endpoints: - - port: monitoring - port: prometheus-dev - port: prometheus-prod namespaceSelector: @@ -17,11 +39,11 @@ spec: - create-sm-prometheus selector: matchLabels: + app.kubernetes.io/component: opentelemetry-collector app.kubernetes.io/instance: create-sm-prometheus.simplest app.kubernetes.io/managed-by: opentelemetry-operator app.kubernetes.io/part-of: opentelemetry - app.kubernetes.io/component: opentelemetry-collector - operator.opentelemetry.io/collector-monitoring-service: "Exists" + operator.opentelemetry.io/collector-service-type: base --- apiVersion: v1 kind: Service @@ -32,6 +54,7 @@ metadata: app.kubernetes.io/managed-by: opentelemetry-operator app.kubernetes.io/name: simplest-collector-monitoring app.kubernetes.io/part-of: opentelemetry + operator.opentelemetry.io/collector-service-type: "monitoring" operator.opentelemetry.io/collector-monitoring-service: "Exists" name: simplest-collector-monitoring namespace: create-sm-prometheus diff --git a/tests/e2e-prometheuscr/create-sm-prometheus-exporters/05-error.yaml b/tests/e2e-prometheuscr/create-sm-prometheus-exporters/05-error.yaml index ecb59ba1fd..c97ae2597d 100644 --- a/tests/e2e-prometheuscr/create-sm-prometheus-exporters/05-error.yaml +++ b/tests/e2e-prometheuscr/create-sm-prometheus-exporters/05-error.yaml @@ -1,5 +1,28 @@ apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor +metadata: + labels: + app.kubernetes.io/instance: create-sm-prometheus.simplest + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/name: simplest-monitoring-collector + name: simplest-monitoring-collector + namespace: create-sm-prometheus +spec: + endpoints: + - port: monitoring + namespaceSelector: + matchNames: + - create-sm-prometheus + selector: + matchLabels: + app.kubernetes.io/component: opentelemetry-collector + app.kubernetes.io/instance: create-sm-prometheus.simplest + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/part-of: opentelemetry + operator.opentelemetry.io/collector-service-type: monitoring +--- +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor metadata: labels: app.kubernetes.io/instance: create-sm-prometheus.simplest @@ -9,7 +32,6 @@ metadata: namespace: create-sm-prometheus spec: endpoints: - - port: monitoring - port: prometheus-dev - port: prometheus-prod - port: prometheusremotewrite/prometheus @@ -18,5 +40,8 @@ spec: - create-sm-prometheus selector: matchLabels: - app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/component: opentelemetry-collector app.kubernetes.io/instance: create-sm-prometheus.simplest + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/part-of: opentelemetry + operator.opentelemetry.io/collector-service-type: base diff --git a/tests/e2e-prometheuscr/create-sm-prometheus-exporters/06-assert.yaml b/tests/e2e-prometheuscr/create-sm-prometheus-exporters/06-assert.yaml index dcfecf5d81..be594e1a1d 100644 --- a/tests/e2e-prometheuscr/create-sm-prometheus-exporters/06-assert.yaml +++ b/tests/e2e-prometheuscr/create-sm-prometheus-exporters/06-assert.yaml @@ -1,5 +1,28 @@ apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor +metadata: + labels: + app.kubernetes.io/instance: create-sm-prometheus.simplest + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/name: simplest-monitoring-collector + name: simplest-monitoring-collector + namespace: create-sm-prometheus +spec: + endpoints: + - port: monitoring + namespaceSelector: + matchNames: + - create-sm-prometheus + selector: + matchLabels: + app.kubernetes.io/component: opentelemetry-collector + app.kubernetes.io/instance: create-sm-prometheus.simplest + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/part-of: opentelemetry + operator.opentelemetry.io/collector-service-type: monitoring +--- +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor metadata: labels: app.kubernetes.io/instance: create-sm-prometheus.simplest @@ -9,7 +32,6 @@ metadata: namespace: create-sm-prometheus spec: endpoints: - - port: monitoring - port: prometheus-dev namespaceSelector: matchNames: @@ -20,7 +42,7 @@ spec: app.kubernetes.io/instance: create-sm-prometheus.simplest app.kubernetes.io/managed-by: opentelemetry-operator app.kubernetes.io/part-of: opentelemetry - operator.opentelemetry.io/collector-monitoring-service: "Exists" + operator.opentelemetry.io/collector-service-type: base --- apiVersion: v1 kind: Service @@ -31,6 +53,7 @@ metadata: app.kubernetes.io/managed-by: opentelemetry-operator app.kubernetes.io/name: simplest-collector-monitoring app.kubernetes.io/part-of: opentelemetry + operator.opentelemetry.io/collector-service-type: "monitoring" operator.opentelemetry.io/collector-monitoring-service: "Exists" name: simplest-collector-monitoring namespace: create-sm-prometheus diff --git a/versions.txt b/versions.txt index 0d32fe228e..76dd1c2098 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.99.0 +opentelemetry-collector=0.100.0 # Represents the current release of the OpenTelemetry Operator. -operator=0.99.0 +operator=0.100.0 # Represents the current release of the Target Allocator. -targetallocator=0.99.0 +targetallocator=0.100.0 # Represents the current release of the Operator OpAMP Bridge. -operator-opamp-bridge=0.99.0 +operator-opamp-bridge=0.100.0 # Represents the current release of Java instrumentation. # Should match autoinstrumentation/java/version.txt @@ -19,7 +19,7 @@ autoinstrumentation-java=1.32.1 # Represents the current release of NodeJS instrumentation. # Should match value in autoinstrumentation/nodejs/package.json -autoinstrumentation-nodejs=0.49.1 +autoinstrumentation-nodejs=0.51.0 # Represents the current release of Python instrumentation. # Should match value in autoinstrumentation/python/requirements.txt